home *** CD-ROM | disk | FTP | other *** search
Text File | 1989-12-05 | 61.7 KB | 1,879 lines |
- % This is TANGLE.CHG for TURBO Pascal 5.5
- %
- % (c) 1989 by Peter Sawatzki <FE617@DHAFEU11.BITNET>
- % Buchenhof 3, D-5800 Hagen 1 (Dahl), Germany (West)
- %
- % Change History:
- %
- % Initials: PS = Peter Sawatzki, FE617@DHAFEU11
- % ========= WGS = Wayne G. Sullivan, WSULIVAN@IRLEARN
- % PB = Peter Breitenlohner, PEB@DM0MPI11
- %
- % rel. date Author description
- % ==== ==== ====== ===========
- % v0.1 2-Mar-88 PS initial TP3 release
- % v0.2 5-May-88 PS array-like macros: "()" solution
- % v0.3 22-Aug-88 PS copy some Inlines from WGS
- % v0.4 3-Sep-88 PS better handling of shl and shr
- % v0.5 8-Dec-88 PS better array-like macros based on PB's solution
- % v0.6 10-Dec-88 PS TurboPascal-like hex constants
- % v0.7 3-Jun-89 PS include the mod/and, div/shr optimization
- % v0.8 29-Jul-89 PS @i option: (nested) include files
- % v0.9 1-Aug-89 PS multiple change files
- % v1.0 3-Aug-89 PS Inline assembler implemented
- % v1.1 5-Dec-89 PS kill error in multiple change file handling
- %
- % Tangle/Compile Instructions:
- % ============================
- % TANGLE TANGLE /d /m /c
- % TPC /$A+,O-,E-,N-,B-,I-,V-,S-,D- /$M$5000,0,$2000 TANGLE /M
- %
- %
- % kludge fillchar firstvar -> lastvar inserted
- %
- ────────────────────────────────────────────────────────────────
- @x l.22 m.0
- \def\PASCAL{Pascal}
- @y
- \def\PASCAL{Pascal}
- \def\TP{\hbox{Turbo Pascal 5.5}}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.36 m.0
- \vfill}
- @y
- \centerline{(Changes for \TP, 5-Dec-89)}
- \vfill}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.64 m.1
- @d banner=='This is TANGLE, Version 4'
- @y
- @d banner=='This is TANGLE, Version 4/TP55 1.1'
- @z
- ────────────────────────────────────────────────────────────────
- @x l.75 m.2
- @d end_of_TANGLE = 9999 {go here to wrap it up}
- @y
- @z
- ────────────────────────────────────────────────────────────────
- @x l.77 m.2
- @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@>@/
- @y
- @p program TANGLE;
- uses
- Asm2Inl;
- const @<Constants in the outer block@>@/
- type @<Types in the outer block@>@/
- const @<Typed constants in the outer block@>@/
- var @/
- firstvar: byte; @/
- @<Globals in the outer block@>@/
- lastvar: byte; @/
- @<Inline procedures and functions@>@/
- @<All purpose procedures and functions@>@/
- @z
- ────────────────────────────────────────────────────────────────
- @x l.94 m.3
- @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
- @d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
- @y
- @d ifdef(#)==@={$ifdef @>#@=}@>
- @d endif==@={$endif}@>
- @d debug==ifdef(deb)
- @d gubed==endif
- @d Asm(#)==inline(@[#@])
- @z
- ────────────────────────────────────────────────────────────────
- @x l.99 m.3
- @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}
- @y
- @d stat==ifdef(sta)
- @d tats==endif
- @z
- ────────────────────────────────────────────────────────────────
- @x l.114 m.4
- @<Compiler directives@>=
- @{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
- @!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
- @y
- @<Inline proc...@>=
- function mavail: word;
- Asm(mov ah,$48/ {allocate memory}
- mov bx,$FFFF/ {determine free memory}
- int $21/
- mov ax,bx); {return size of largest available block}
-
- function malloc (no: word): word;
- Asm(mov ah,$48/ {allocate memory}
- pop bx/ {no of bytes}
- int $21/
- jnc ok/ {no error}
- xor ax,ax/ {clear ax in case of error}
- ok: );
-
- procedure mfree (segm: word);
- Asm(mov ah,$49/ {free memory}
- pop es/ {segment to free}
- int $21);
- @z
- ────────────────────────────────────────────────────────────────
- @x l.139 m.6
- @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
- @y
- @d incr(#) == Inc(#) {increase a variable by unity}
- @d decr(#) == Dec(#) {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 == @= exit @>
- @f return == nil
- @f loop == xclause
- @d void == begin end
- @z
- ────────────────────────────────────────────────────────────────
- @x l.168 m.7
- @d othercases == others: {default for cases not listed explicitly}
- @y
- @d othercases == else {default for cases not listed explicitly}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.177 m.8
- @!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}
- @y
- @!buf_size=256; {maximum length of input line (must be |>255| for inline code)}
- @!max_max_bytes=8000;
- @!min_bytes=1000;
- @!step_bytes=1000;
- @!max_bytes: word = max_max_bytes;
- {|1/ww| times the number of bytes in identifiers,
- strings, and module names; must be less than 65536}
- @!max_max_toks = 15000;
- @!min_toks = 2000;
- @!step_toks = 2000;
- @!max_toks: word = max_max_toks;
- {|1/zz| times the number of bytes in compressed \PASCAL\ code;
- must be less than 65536}
- @!max_names=4600; {number of identifiers, strings, module names;
- must be less than 10240}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.190 m.8
- @!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}
- @y
- @!max_id_length=30; {long identifiers are chopped to this length, which must
- not exceed |line_length|}
- @!unambig_length=25; {identifiers must be unique if chopped to this length}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.301 m.12
- @!text_file=packed file of text_char;
- @y
- @!text_file=Text;
- @ @d term_out==Output
- @z
- ────────────────────────────────────────────────────────────────
- @x l.303 m.13
- @ 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}
- @y
- @ 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.
- The following typed constants define the |xchr| array properly.
-
- @<Typed constants...@>=
- xchr: array [ASCII_code] of text_char=(@/
- ' ',' ',' ',' ',' ',' ',' ',' ', ' ', #9,' ',' ',' ',#13,' ',' ',@/
- ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',@/
- ' ','!','"','#','$','%','&','''', '(',')','*','+',',','-','.','/',@/
- '0','1','2','3','4','5','6','7', '8','9',':',';','<','=','>','?',@/
- '@@','A','B','C','D','E','F','G', 'H','I','J','K','L','M','N','O',@/
- 'P','Q','R','S','T','U','V','W', 'X','Y','Z','[','\',']','^','_',@/
- '`','a','b','c','d','e','f','g', 'h','i','j','k','l','m','n','o',@/
- 'p','q','r','s','t','u','v','w', 'x','y','z','{','|','}','~',' ',@/
- ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',@/
- ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',@/
- ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',@/
- ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',@/
- ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',@/
- ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',@/
- ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',@/
- ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ');
-
- @ The following definition makes the |xord| array contain a
- suitable inverse to the information in |xchr|.
-
- @<Globals...@>=
- @!xord: array [text_char] of ASCII_code absolute xchr;
- {specifies conversion of input characters}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.443 m.16
- @ 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[' ']:=" ";
- @y
- @ not neccesssary
-
- @ not neccessary
-
- @ not neccessary
- @z
- ────────────────────────────────────────────────────────────────
- @x l.511 m.20
- @!term_out:text_file; {the terminal as an output file}
- @y
- @z
- ────────────────────────────────────────────────────────────────
- @x l.519 m.21
- rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
- @y
- @z
- ────────────────────────────────────────────────────────────────
- @x l.526 m.22
- @d update_terminal == break(term_out) {empty the terminal output buffer}
- @y
- @d update_terminal ==
- @z
- ────────────────────────────────────────────────────────────────
- @x l.528 m.23
- @ The main input comes from |web_file|; this input may be overridden
- by changes in |change_file|. (If |change_file| is empty, there are no changes.)
-
- @<Globals...@>=
- @!web_file:text_file; {primary input}
- @!change_file:text_file; {updates}
- @y
- @ We need some data structures to implement the include facility of
- \.{TANGLE}
- @<Constants...@>=
- ChangeMax = 5; {maximal # of Changefiles}
- No_of_Files = 6; {Webfile + Changefiles + all Include files}
- buffer_size = 4*1024; {multiple of 16}
-
- @ @<Globals...@>=
- FileMax: 0..No_of_Files; {how many files fit into memory}
- @!file_prev: array[1..No_of_Files] of word;
- @!file_ptr: array[1..No_of_Files] of word;
-
- @ To access a textfile we use
- @d textf(#)==Text(Ptr(file_ptr[#],0)^)
-
- @ The main input comes from |web_file|; this input may be overridden
- by changes in the |ChgFile|s. |ChgCnt| is the number of change files.
- (If |ChgCnt| is zero, there are no changes.)
-
- @<Globals...@>=
- @!web_file:word; {primary input}
- @!ChgFile: array[1..ChangeMax] of word; {array of change files}
- @!ChgCnt: 0..ChangeMax; { # of change files in |ChgFile|}
- @!ChgAct: array[1..ChangeMax] of 1..ChangeMax; {active change files}
- @!ChgLevel: 0..ChangeMax; {# of active change files in |ChgAct|}
-
- @z
- ────────────────────────────────────────────────────────────────
- @x l.535 m.24
- @ The following code opens the input files. Since these files were listed
- in the program header, we assume that the \PASCAL\ runtime system has
- already checked that suitable file names have been given; therefore no
- additional error checking needs to be done.
- @^system dependencies@>
-
- @p procedure open_input; {prepare to read |web_file| and |change_file|}
- begin reset(web_file); reset(change_file);
- end;
- @y
- @ The following code closes an input file. If the input file is the son
- of another input file, the higher level file is returned.
- @^system dependencies@>
-
- @p procedure close_fil (var f: word);
- var tf: word;
- begin
- if f<>0 then begin
- Close(textf(f));
- if IoResult<>0 then void;
- tf:= f;
- f:= file_prev[f];
- file_prev[tf]:= 0; { buffer now available }
- if f=$FFFF then f:= 0;
- end
- end;
-
- @ Next we need a function to open an input file. Checks will be
- made to verify that there are not too many include files open.
-
- @p function open_fil (var f: word; name: String): boolean;
- var
- tf: word;
- begin
- open_fil:= false;
- tf:= 1;
- while (tf<=FileMax) and (file_prev[tf]<>0) do
- incr(tf);
- if tf>FileMax then
- fatal_halt('@@i ',name,': no more than ',FileMax,' open files.');
- assign(textf(tf),name);
- SetTextBuf(textf(tf),Ptr(file_ptr[tf],128)^,buffer_size);
- reset(textf(tf));
- if IoResult=0 then begin
- open_fil:= true;
- if f=0 then f:= $FFFF;
- file_prev[tf]:= f;
- f:= tf
- end
- end;
-
- @ @<Close all Files@>=
- while web_file>0 do close_fil(web_file);
- for ChgLevel:= 1 to ChgCnt do
- while ChgFile[ChgLevel]>0 do close_fil(ChgFile[ChgLevel]);
-
- @ The following code initializes the input buffers
- @<Local variables for init...@>=
- tf: word;
-
- @ @<Set init...@>=
- FileMax:= No_of_Files;
- repeat
- file_ptr[1]:= malloc(((buffer_size+128) shr 4) *FileMax);
- if file_ptr[1]=0 then decr(FileMax)
- until (file_ptr[1]<>0) or (FileMax<2);
- if file_ptr[1]=0 then
- fatal_halt('No memory for the webfile and a changefile.');
- tf:= 2;
- while tf<=No_of_Files do begin
- file_ptr[tf]:= file_ptr[tf-1]+(buffer_size+128) shr 4;
- incr(tf)
- end;
-
- @ We need a procedure to force the extension in a filename.
- @<Inline proc...@>=
- function ForceExtension (FName, FExt : String): String;
- {-Return a pathname with the specified extension attached}
- var
- i,DotPos,BackSlashPos: byte;
- begin
- DotPos := 0;
- for i := 1 to _Length(FName) do begin
- if FName[I]='.' then DotPos := i;
- if FName[i]='\' then BackSlashPos:= i;
- end;
- if DotPos>BackSlashPos then
- ForceExtension:= _copy(FName,1,DotPos)+FExt
- else
- ForceExtension := FName+'.'+FExt;
- end;
-
- @ Now we open the |web_file| and the |ChgFile|s if present.
- @p procedure open_input; {prepare to read |web_file| and |ChgFile|}
- var
- fn: String;
- tf,pc: word;
- begin
- for tf:= 1 to No_of_Files do
- file_prev[tf]:= 0;
- web_file:= 0;
- if not open_fil(web_file,Parameter(1)) then
- if not open_fil(web_file,ForceExtension(Parameter(1),'WEB')) then
- fatal_halt('WEB file not found');
- fn:= Parameter(2); if (fn='') or (fn='*') then fn:= Parameter(1);
- ChgCnt:= 0; pc:= 2;
- while fn<>'' do begin
- if open_fil(ChgFile[ChgCnt+1],fn) then
- incr(ChgCnt)
- else
- if open_fil(ChgFile[ChgCnt+1],ForceExtension(fn,'CHG')) then
- incr(ChgCnt)
- else
- print_ln('CHG file ',fn,' not found.');
- incr(pc);
- fn:= Parameter(pc)
- end
- end;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.549 m.25
- @!Pascal_file: text_file;
- @y
- @!Pascal_file: text_file;
- @!Pascal_buffer: word;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.558 m.26
- @<Set init...@>=
- rewrite(Pascal_file); rewrite(pool);
- @y
- @<Close all...@>=
- @!Close(Pascal_file);
- @
- @<Set init...@>=
- Pascal_buffer:= malloc(8192 shr 4);
- if Pascal_buffer=0 then fatal_halt('No buffer for the pascal file.');
- assign(Pascal_file,ForceExtension(Parameter(1),'PAS'));
- SetTextBuf(Pascal_file,Ptr(Pascal_buffer,0)^,8192);
- rewrite(Pascal_file);
- if IoResult>0 then fatal_halt('Unable to create ',ForceExtension(Parameter(1),'PAS'));
- assign(pool,Parameter(1)+'.POO'); rewrite(pool);
- @z
- ────────────────────────────────────────────────────────────────
- @x l.579 m.28
- @p function input_ln(var f:text_file):boolean;
- {inputs a line or returns |false|}
- var final_limit:0..buf_size; {|limit| without trailing blanks}
- begin limit:=0; final_limit:=0;
- if eof(f) then input_ln:=false
- else begin while not eoln(f) do
- begin buffer[limit]:=xord[f^]; get(f);
- incr(limit);
- if buffer[limit-1]<>" " then final_limit:=limit;
- if limit=buf_size then
- begin while not eoln(f) do get(f);
- decr(limit); {keep |buffer[buf_size]| empty}
- if final_limit>limit then final_limit:=limit;
- print_nl('! Input line too long'); loc:=0; error;
- @.Input line too long@>
- end;
- end;
- read_ln(f); limit:=final_limit; input_ln:=true;
- end;
- end;
- @y
- @p function input_ln (var f: word):boolean;
- label
- new_file;
- var
- s: String;
- fileend: boolean;
- i: byte;
-
- procedure open_include;
- var
- i: byte;
- fn: String;
- begin
- i:= 4;
- while (i<=Length(s)) and (s[i]<>' ') do incr(i);
- byte(fn[0]):= i-4;
- move(s[4],fn[1],Length(fn));
- if not open_fil(f,fn) then
- if not open_fil(f,fn+'.CHI') then
- if not open_fil(f,fn+'.CHG') then
- fatal_halt('@@i ',fn,': Include file not found.')
- end;
-
- begin new_file:
- limit:= 0;
- fileend:= eof(textf(f));
- if IoResult>0 then fileend:= true;
- if fileend then begin
- close_fil(f);
- if f>0 then
- goto new_file
- else
- input_ln:= false
- end else begin
- readln(textf(f),s);
- limit:= byte(s[0]);
- if (limit>3) and (s[1]='@@') and (s[2]='i') and (s[3]=' ') then begin
- open_include;
- goto new_file
- end;
- while (limit>0) and (s[limit]=' ') do decr(limit);
- for i:= 1 to limit do buffer[i-1]:= xord[s[i]];
- input_ln:=true;
- end;
- end;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.651 m.32
- @<Print error location based on input buffer@>=
- begin if changing then print('. (change file ')@+else print('. (');
- print_ln('l.', line:1, ')');
- @y
- @<Print error location based on input buffer@>=
- begin
- if ChgLevel>0 then
- print('. (change file #',ChgAct[ChgLevel],
- ' l.',ChgLine[ChgAct[ChgLevel]]:1)
- else
- print('. (l.', line:1);
- print_ln(')');
- @z
- ────────────────────────────────────────────────────────────────
- @x l.685 m.34
- @d fatal_error(#)==begin new_line; print(#); error; mark_fatal; jump_out;
- end
-
- @<Error handling...@>=
- procedure jump_out;
- begin goto end_of_TANGLE;
- end;
- @y
- @d fatal_error(#)==begin new_line; print(#); error; mark_fatal; Halt(history) end
- @d fatal_halt(#)==begin new_line; print(#); mark_fatal; Halt(history) end
- @z
- ────────────────────────────────────────────────────────────────
- @x l.698 m.35
- @d confusion(#)==fatal_error('! This can''t happen (',#,')')
- @.This can't happen@>
-
- @ An overflow stop occurs if \.{TANGLE}'s tables aren't large enough.
-
- @d overflow(#)==fatal_error('! Sorry, ',#,' capacity exceeded')
- @.Sorry, x capacity exceeded@>
- @y
- @p procedure confusion(s: String);
- begin fatal_error('! This can''t happen (',s,')')
- @.This can't happen@>
- end;
-
- @ An overflow stop occurs if \.{TANGLE}'s tables aren't large enough.
-
- @p procedure overflow(s: String);
- begin fatal_error('! Sorry, ',s,' capacity exceeded')
- @.Sorry, x capacity exceeded@>
- end;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.736 m.38
- @d ww=2 {we multiply the byte capacity by approximately this amount}
- @d zz=3 {we multiply the token capacity by approximately this amount}
- @y
- @d ww=16 {we multiply the byte capacity by approximately this amount}
- @d zz=16 {we multiply the token capacity by approximately this amount}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.739 m.38
- @<Globals...@>=
- @!byte_mem: packed array [0..ww-1,0..max_bytes] of ASCII_code;
- {characters of names}
- @y
- @ @<Set init...@>=
- if (ww<>16) then
- fatal_halt('! ww must be 16 (segment size).');
- free:= mavail;
- while (max_bytes+max_toks>free)
- and (max_bytes>min_bytes) and (max_toks>min_toks) do begin
- decr(max_bytes,step_bytes);
- decr(max_toks,step_toks)
- end;
- byte_seg:= malloc(max_bytes);
- tok_seg:= malloc(max_toks);
- if byte_seg=0 then
- fatal_halt('! no memory for byte_mem');
- if tok_seg=0 then
- fatal_halt('! no memory for tok_mem');
-
- @ @<Inline...@>=
- function bytem (s,o: word): Pointer;@/
- Asm(pop dx/
- pop ax/
- add dx,[>byte_seg]);
-
- @ @d byte_mem[#]==ASCII_code(bytem(#)^)
- @ @<Globals...@>=
- @t\hskip1em@>@!byte_seg: word;
- @!free: word;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.742 m.38
- @!tok_mem: packed array [0..zz-1,0..max_toks] of eight_bits; {tokens}
- @y
- @ @<Set init...@>=
- if (zz<>16) then
- fatal_halt('! zz must be 16 (segment size).');
-
- @ @<Inline...@>=
- function tokm (s,o: word): Pointer;@/
- Asm(pop dx/
- pop ax/
- add dx,[>tok_seg]);
-
- @ @d tok_mem[#]==eight_bits(tokm(#)^)
- @ @<Globals...@>=
- @t\hskip1em@>@!tok_seg: word;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.788 m.40
- @!byte_ptr:array [0..ww-1] of 0..max_bytes;
- {first unused position in |byte_mem|}
- @!pool_check_sum:integer; {sort of a hash for the whole string pool}
- @y
- @!byte_ptr:array [0..ww-1] of 0..max_max_bytes;
- {first unused position in |byte_mem|}
- @!pool_check_sum:LongInt; {sort of a hash for the whole string pool}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.828 m.44
- @t\hskip1em@>@!tok_ptr:array[0..zz-1] of 0..max_toks;
- {first unused position in a given segment of |tok_mem|}
- @y
- @t\hskip1em@>@!tok_ptr:array[0..zz-1] of 0..max_max_toks;
- {first unused position in a given segment of |tok_mem|}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.831 m.44
- stat @!max_tok_ptr:array[0..zz-1] of 0..max_toks;
- {largest values assumed by |tok_ptr|}
- tats
- @y
- stat @!max_tok_ptr:array[0..zz-1] of 0..max_max_toks;
- {largest values assumed by |tok_ptr|}
- tats
- @z
- ────────────────────────────────────────────────────────────────
- @x l.863 m.47
- like simple identifiers, their |equiv| value points to the replacement text.
- @y
- like simple identifiers, their |equiv| value points to the replacement text.
-
- \yskip\hang |array_type| identifiers have been defined to be array-type macros;
- they are like parametric identifiers but the macro parameters and
- arguments are enclosed in square brackets.
- @z
- ────────────────────────────────────────────────────────────────
- @x l.868 m.47
- @d parametric=3 {parametric macros have |parametric| ilk}
- @y
- @d parametric=3 {parametric macros have |parametric| ilk}
- @d array_type=4 {array-type macros have |array_type| ilk}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.890 m.49
- var k:0..max_bytes; {index into |byte_mem|}
- @y
- var k:0..max_max_bytes; {index into |byte_mem|}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.961 m.53
- @!k:0..max_bytes; {index into |byte_mem|}
- @y
- @!k:0..max_max_bytes; {index into |byte_mem|}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.979 m.54
- h:=buffer[id_first]; i:=id_first+1;
- while i<id_loc do
- begin h:=(h+h+buffer[i]) mod hash_size; incr(i);
- end
- @y
- Asm(_cld/
- _mov bx,>hash_size/
- _xor ax,ax/
- _xor dx,dx/
- _mov si,>buffer/
- _add si,[>id_first]/
- _mov cx,[bp+<l]/
- lp1:_shl dx,1/
- _lodsb/
- _add dx,ax/
- lp2:_sub dx,bx/
- _jge lp2/
- _add dx,bx/
- _loop lp1/
- _mov [bp+<h],dx);
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1024 m.58 - make underscore significant
- begin if buffer[i]<>"_" then
- begin if buffer[i]>="a" then chopped_id[s]:=buffer[i]-@'40
- else chopped_id[s]:=buffer[i];
- h:=(h+h+chopped_id[s]) mod hash_size; incr(s);
- end;
- incr(i);
- @y
- begin
- chopped_id[s]:=buffer[i];
- h:=(h+h+chopped_id[s]) mod hash_size; incr(s);
- incr(i);
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1096 m.63 - make underscore significant
- if c<>"_" then
- begin if c>="a" then c:=c-@'40; {merge lowercase with uppercase}
- if chopped_id[s]<>c then goto not_found;
- incr(s);
- end;
- @y
- if chopped_id[s]<>c then goto not_found;
- incr(s);
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1168 m.66
- @!k:0..max_bytes; {index into |byte_mem|}
- @y
- @!k:0..max_max_bytes; {index into |byte_mem|}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1222 m.69
- @!k:0..max_bytes; {index into |byte_mem|}
- @y
- @!k:0..max_max_bytes; {index into |byte_mem|}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1324 m.72 - implement an inline assembler
- @d join=@'177 {ASCII delete will not appear}
- @y
- @d join=@'177 {ASCII delete will not appear}
- @d asm_start=@'16 {ASCII SO will not appear}
- @d asm_end=@'17 {ASCII SI will not appear}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1332 m.73
- tok_mem[z,tok_ptr[z]]:=x div@'400; {this could be done by a shift command}
- tok_mem[z,tok_ptr[z]+1]:=x mod@'400; {this could be done by a logical and}
- @y
- tok_mem[z,tok_ptr[z]]:=hi(x); tok_mem[z,tok_ptr[z]+1]:=lo(x);
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1495 m.85
- begin if ilk[cur_name]=parametric then
- @y
- begin if ilk[cur_name]>=parametric then
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1527 m.86
- @!cur_val:integer; {additional information corresponding to output token}
- @y
- @!cur_val:LongInt; {additional information corresponding to output token}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1536 m.87
- @!k:0..max_bytes; {index into |byte_mem|}
- @y
- @!k:0..max_max_bytes; {index into |byte_mem|}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1546 m.87 - implement an inline assembler
- a:=tok_mem[zo,cur_byte]; incr(cur_byte);
- @y
- a:=tok_mem[zo,cur_byte]; incr(cur_byte);
- case a of
- asm_start,asm_end: goto found;
- end;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1579 m.89
- numeric: begin cur_val:=equiv[a]-@'100000; a:=number;
- @y
- numeric: begin cur_val:=LongInt(equiv[a])-@'100000; a:=number;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1583 m.89
- parametric: begin @<Put a parameter on the parameter stack,
- @y
- parametric,array_type: begin @<Put a parameter on the parameter stack,
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1598 m.90
- @<Put a parameter...@>=
- while (cur_byte=cur_end)and(stack_ptr>0) do pop_level;
- if (stack_ptr=0)or(tok_mem[zo,cur_byte]<>"(") then
- begin print_nl('! No parameter given for '); print_id(a); error;
- @.No parameter given for macro@>
- goto restart;
- end;
- @<Copy the parameter into |tok_mem|@>;
- @y
- @d NoParam(#)== begin print_nl(#); print_id(a); error; goto restart end
- @<Put a parameter...@>=
- while (cur_byte=cur_end)and(stack_ptr>0) do pop_level;
- if stack_ptr=0 then NoParam('! No parameter given for ');
- case ilk[a] of
- parametric: if tok_mem[zo,cur_byte]="(" then begin
- @<Copy the parameter into |tok_mem|@>
- end else
- NoParam('! No parameter given for ');
- array_type: if tok_mem[zo,cur_byte]="[" then begin
- @<Copy the array parameter into |tok_mem|@>
- end else
- NoParam('! No array parameter given for ');
- end;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1783 m.95
- @!out_val,@!out_app:integer; {pending values}
- @y
- @!out_val,@!out_app:LongInt; {pending values}
- @!assembling: boolean; {true, when parsing/expanding assembler text}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1791 m.96
- out_state:=misc; out_ptr:=0; break_ptr:=0; semi_ptr:=0; out_buf[0]:=0; line:=1;
- @y
- out_state:=misc; out_ptr:=0; break_ptr:=0; semi_ptr:=0; out_buf[0]:=0; line:=1;
- assembling:= false;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1799 m.97
- @d check_break==if out_ptr>line_length then flush_buffer
- @y
- @d check_break==if out_ptr>line_length then flush_buffer
- @d im_break==begin
- break_ptr:= out_ptr;
- semi_ptr:= 0;
- flush_buffer
- end
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1806 m.97 -implement an inline assembler
- for k:=1 to break_ptr do write(Pascal_file,xchr[out_buf[k-1]]);
- write_ln(Pascal_file); incr(line);
- if line mod 100 = 0 then
- begin print('.');
- if line mod 500 = 0 then print(line:1);
- update_terminal; {progress report}
- end;
- if break_ptr<out_ptr then
- begin if out_buf[break_ptr]=" " then
- begin incr(break_ptr); {drop space at break}
- if break_ptr>b then b:=break_ptr;
- end;
- for k:=break_ptr to out_ptr-1 do out_buf[k-break_ptr]:=out_buf[k];
- end;
- out_ptr:=out_ptr-break_ptr; break_ptr:=b-break_ptr; semi_ptr:=0;
- if out_ptr>line_length then
- begin err_print('! Long line must be truncated'); out_ptr:=line_length;
- @.Long line must be truncated@>
- end;
- @y
- if assembling then begin
- for k:= 1 to out_ptr do
- if not FeedAsm(xchr[out_buf[k-1]]) then
- overflow('inline input');
- out_ptr:= 0
- end else begin
- for k:= 1 to break_ptr do write(Pascal_file,xchr[out_buf[k-1]]);
- write_ln(Pascal_file); incr(line);
- if line mod 100 = 0 then
- begin print('.');
- if line mod 500 = 0 then print(line:1);
- update_terminal; {progress report}
- end;
- if break_ptr<out_ptr then
- begin if out_buf[break_ptr]=" " then
- begin incr(break_ptr); {drop space at break}
- if break_ptr>b then b:=break_ptr;
- end;
- move(out_buf[break_ptr],out_buf[0],out_ptr-break_ptr);
- end;
- decr(out_ptr,break_ptr); break_ptr:=b-break_ptr; semi_ptr:=0;
- if out_ptr>line_length then
- begin err_print('! Long line must be truncated'); out_ptr:=line_length;
- @.Long line must be truncated@>
- end;
- end;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1839 m.99
- @p procedure app_val(@!v:integer); {puts |v| into buffer, assumes |v>=0|}
- @y
- @p procedure app_val(@!v:LongInt); {puts |v| into buffer, assumes |v>=0|}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1881 m.101
- if t<>misc then for k:=1 to v do app(out_contrib[k])
- @y
- if t<>misc then begin
- move(out_contrib,out_buf[out_ptr],v); incr(out_ptr,v)
- end
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1930 m.105
- (((out_contrib[1]="D")and(out_contrib[2]="I")and(out_contrib[3]="V")) or@|
- ((out_contrib[1]="M")and(out_contrib[2]="O")and(out_contrib[3]="D")) ))or@|
- @y
- (((out_contrib[1]="d")and(out_contrib[2]="i")and(out_contrib[3]="v")) or@|
- ((out_contrib[1]="a")and(out_contrib[2]="n")and(out_contrib[3]="d")) or@|
- ((out_contrib[1]="s")and(out_contrib[2]="h")and
- ((out_contrib[3]="l")or(out_contrib[3]="r"))) or@|
- ((out_contrib[1]="m")and(out_contrib[2]="o")and(out_contrib[3]="d")) ))or@|
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1941 m.106
- @p procedure send_sign(@!v:integer);
- @y
- @p procedure send_sign(@!v:LongInt);
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1959 m.107
- @p procedure send_val(@!v:integer); {output the (signed) value |v|}
- @y
- @p procedure send_val(@!v:LongInt); {output the (signed) value |v|}
- var potcnt: byte;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.1995 m.110 - optimize DIV and MOD
- @ @<If previous output was \.{DIV}...@>=
- if (out_ptr=break_ptr+3)or
- ((out_ptr=break_ptr+4)and(out_buf[break_ptr]=" ")) then
- @^uppercase@>
- if ((out_buf[out_ptr-3]="D")and(out_buf[out_ptr-2]="I")and
- (out_buf[out_ptr-1]="V"))or @/
- ((out_buf[out_ptr-3]="M")and(out_buf[out_ptr-2]="O")and
- (out_buf[out_ptr-1]="D")) then@/ goto bad_case
- @y
- @ @<Typed constants...@>=
- modopt: boolean = false;
- divopt: boolean = false;
- InsComments: boolean = false;
-
- @ @<Local variables for init...@>=
- pi,pp: word;
- s: String;
- @ @<Set init...@>=
- for pi:= 1 to ParamCount do begin
- s:= ParamStr(pi);
- pp:= pos('-',s);
- if pp=0 then pp:= pos('/',s);
- if pp>0 then case UpCase(s[pp+1]) of
- 'D': divopt:= true;
- 'M': modopt:= true;
- 'C': InsComments:= true;
- end
- end;
-
- @ @<If previous output was \.{DIV}...@>=
- begin if (out_ptr=break_ptr+3)or
- ((out_ptr=break_ptr+4)and(out_buf[break_ptr]=" ")) then
- @^uppercase@>
- begin
- if ((out_buf[out_ptr-3]="d")and(out_buf[out_ptr-2]="i")and
- (out_buf[out_ptr-1]="v") and divopt) then begin
- if (v>0) and (v<257) and (v and (v-1)=0) then begin
- potcnt:= 0;
- while lo(v) and 1=0 do begin
- incr(potcnt);
- v:= v shr 1
- end;
- v:= potcnt;
- out_buf[out_ptr-3]:= "s";
- out_buf[out_ptr-2]:= "h";
- out_buf[out_ptr-1]:= "r";
- end;
- goto bad_case
- end;
- if ((out_buf[out_ptr-3]="m")and(out_buf[out_ptr-2]="o")and
- (out_buf[out_ptr-1]="d")and modopt) then begin
- if (v>0) and (v and (v-1)=0) then begin
- Dec(v);
- out_buf[out_ptr-3]:= "a";
- out_buf[out_ptr-2]:= "n";
- out_buf[out_ptr-1]:= "d";
- end;
- goto bad_case
- end;
- if ((out_buf[out_ptr-3]="s")and(out_buf[out_ptr-2]="h")and
- (out_buf[out_ptr-1]="l")) or @/
- ((out_buf[out_ptr-3]="s")and(out_buf[out_ptr-2]="h")and
- (out_buf[out_ptr-1]="r")) then@/ goto bad_case
- end
- end
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2042 m.113
- @!j:0..max_bytes; {index into |byte_mem|}
- @!w:0..ww-1; {segment of |byte_mem|}
- @!n:integer; {number being scanned}
- @y
- @!j:0..max_max_bytes; {index into |byte_mem|}
- @!w:0..ww-1; {segment of |byte_mem|}
- @!n:LongInt; {number being scanned}
- @!outind: word;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2059 m.113 - implement an inline assembler
- verbatim: @<Send verbatim string@>;
- @y
- verbatim: @<Send verbatim string@>;
- asm_start: if assembling then
- err_print('! Already assembling')
- @.Already assembling@>
- else begin
- SetUpAsm;
- im_break;
- assembling:= true
- end;
- asm_end: if assembling then begin
- send_out(frac,0); {tss, tss}
- im_break;
- assembling:= false;
- if not DoAsm(InsComments) then
- mark_harmless;
-
- for outind:= 0 to ObjSize-1 do
- case byte(TextArray[outind]) of
- 13: im_break;
- 10: do_nothing;
- else
- app(byte(TextArray[outind]))
- end
- end else
- err_print('! Not in assembler mode');
- @.Not in assembler mode@>
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2070 m.114
- and_sign: begin out_contrib[1]:="A"; out_contrib[2]:="N"; out_contrib[3]:="D";
- @y
- and_sign: begin out_contrib[1]:="a"; out_contrib[2]:="n"; out_contrib[3]:="d";
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2074 m.114
- not_sign: begin out_contrib[1]:="N"; out_contrib[2]:="O"; out_contrib[3]:="T";
- @y
- not_sign: begin out_contrib[1]:="n"; out_contrib[2]:="o"; out_contrib[3]:="t";
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2077 m.114
- set_element_sign: begin out_contrib[1]:="I"; out_contrib[2]:="N";
- @y
- set_element_sign: begin out_contrib[1]:="i"; out_contrib[2]:="n";
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2080 m.114
- or_sign: begin out_contrib[1]:="O"; out_contrib[2]:="R"; send_out(ident,2);
- @y
- or_sign: begin out_contrib[1]:="o"; out_contrib[2]:="r"; send_out(ident,2);
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2124 m.116
- @d up_to(#)==#-24,#-23,#-22,#-21,#-20,#-19,#-18,#-17,#-16,#-15,#-14,
- #-13,#-12,#-11,#-10,#-9,#-8,#-7,#-6,#-5,#-4,#-3,#-2,#-1,#
- @y
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2128 m.116
- "A",up_to("Z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
- end;
- "a",up_to("z"): begin out_contrib[1]:=cur_char-@'40; send_out(ident,1);
- end;
- @y
- "A".."Z","a".."z": begin out_contrib[1]:=cur_char; send_out(ident,1); end;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2132 m.116
- identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
- while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
- begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
- if out_contrib[k]>="a" then out_contrib[k]:=out_contrib[k]-@'40
- else if out_contrib[k]="_" then decr(k);
- end;
- @y
- identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
- while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
- begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
- if out_contrib[k]="_" then decr(k);
- end;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2289 m.124 - implement multiple change files
- @ But first we need to consider the low-level routine |get_line|
- that takes care of merging |change_file| into |web_file|. The |get_line|
- procedure also updates the line numbers for error messages.
-
- @<Globals...@>=
- @!line:integer; {the number of the current line in the current file}
- @!other_line:integer; {the number of the current line in the input file that
- is not currently being read}
- @!temp_line:integer; {used when interchanging |line| with |other_line|}
- @!limit:0..buf_size; {the last character position occupied in the buffer}
- @!loc:0..buf_size; {the next character position to be read from the buffer}
- @!input_has_ended: boolean; {if |true|, there is no more input}
- @!changing: boolean; {if |true|, the current line is from |change_file|}
-
- @ As we change |changing| from |true| to |false| and back again, we must
- remember to swap the values of |line| and |other_line| so that the |err_print|
- routine will be sure to report the correct line number.
-
- @d change_changing==
- changing := not changing;
- temp_line:=other_line; other_line:=line; line:=temp_line
- {|line @t$\null\BA\null$@> other_line|}
-
- @ When |changing| is |false|, the next line of |change_file| is kept in
- |change_buffer[0..change_limit]|, for purposes of comparison with the next
- line of |web_file|. After the change file has been completely input, we
- set |change_limit:=0|, so that no further matches will be made.
-
- @<Globals...@>=
- @!change_buffer:array[0..buf_size] of ASCII_code;
- @!change_limit:0..buf_size; {the last position occupied in |change_buffer|}
- @y
- @ But first we need to consider the low-level routine |get_line|
- that takes care of merging |change_file| into |web_file|. The |get_line|
- procedure also updates the line numbers for error messages.
-
- @<Globals...@>=
- @!line:word; {the number of the current line in the current file}
- @!limit:0..buf_size; {the last character position occupied in the buffer}
- @!loc:0..buf_size; {the next character position to be read from the buffer}
- @!input_has_ended: boolean; {if |true|, there is no more input}
-
- @ The next line of the |Chg_File|s is kept in
- |ChgBuffer|, for purposes of comparison with the next
- line of |web_file| or another |ChgFile|. After a change file
- has been completely input, we set the corresponding |ChgLimit|
- to zero, so that no further matches will be made.
-
- @<Globals...@>=
- @!ChgBuffer:array[1..ChangeMax] of array[0..buf_size] of ASCII_code;
- @!ChgLimit: array[1..ChangeMax] of 0..buf_size; {the last positions occupied in |ChgBuffer|}
- @!ChgLine: array[1..ChangeMax] of word; {the line numbers}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2321 m.127 - implement multiple change files
- @ Here's a simple function that checks if the two buffers are different.
-
- @p function lines_dont_match:boolean;
- label exit;
- var k:0..buf_size; {index into the buffers}
- begin lines_dont_match:=true;
- if change_limit<>limit then return;
- if limit>0 then
- for k:=0 to limit-1 do if change_buffer[k]<>buffer[k] then return;
- lines_dont_match:=false;
- exit: end;
-
- @ Procedure |prime_the_change_buffer| sets |change_buffer| in preparation
- for the next matching operation. Since blank lines in the change file are
- not used for matching, we have |(change_limit=0)and not changing| if and
- only if the change file is exhausted. This procedure is called only
- when |changing| is true; hence error messages will be reported correctly.
-
- @p procedure prime_the_change_buffer;
- label continue, done, exit;
- var k:0..buf_size; {index into the buffers}
- begin change_limit:=0; {this value will be used if the change file ends}
- @<Skip over comment lines in the change file; |return| if end of file@>;
- @<Skip to the next nonblank line; |return| if end of file@>;
- @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>;
- exit: end;
-
- @ 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...@>=
- loop@+ begin incr(line);
- if not input_ln(change_file) then return;
- if limit<2 then goto continue;
- if buffer[0]<>"@@" then goto continue;
- if (buffer[1]>="X")and(buffer[1]<="Z") then
- buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
- if buffer[1]="x" then goto done;
- if (buffer[1]="y")or(buffer[1]="z") then
- begin loc:=2; err_print('! Where is the matching @@x?');
- @.Where is the match...@>
- end;
- continue: end;
- done:
-
- @ Here we are looking at lines following the \.{@@x}.
-
- @<Skip to the next nonblank line...@>=
- repeat incr(line);
- if not input_ln(change_file) then
- begin err_print('! Change file ended after @@x');
- @.Change file ended...@>
- return;
- end;
- until limit>0;
- @y
- @ Here's a simple function that checks |buffer| doesn't match a |ChgBuffer|.
-
- @p function lines_dont_match(ci: word):boolean;
- label exit;
- var k:0..buf_size; {index into the buffers}
- begin lines_dont_match:=true;
- if ChgLimit[ci]<>limit then return;
- if limit>0 then
- for k:=0 to limit-1 do if ChgBuffer[ci][k]<>buffer[k] then return;
- lines_dont_match:=false;
- exit: end;
-
- @ Procedure |prime_the_change_buffer| sets a |ChgBuffer| in preparation
- for the next matching operation. Since blank lines in the change files are
- not used for matching, we have |(ChgLimit=0)and not changing| if and
- only if the change file is exhausted. This procedure is called only
- when |changing| is true; hence error messages will be reported correctly.
-
- @p procedure prime_the_change_buffer (ci:word);
- label exit;
- var k:0..buf_size; {index into the buffers}
- begin ChgLimit[ci]:=0; {this value will be used if the change file ends}
- @<Skip over comment lines in the change file; |return| if end of file@>;
- @<Skip to the next nonblank line; |return| if end of file@>;
- ChgLimit[ci]:= limit;
- move(buffer,ChgBuffer[ci],limit);
- exit: end;
-
- @ 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...@>=
- repeat
- incr(ChgLine[ci]);
- if not input_ln(ChgFile[ci]) then return;
- if (limit>=2) and (buffer[0]="@@") then
- case buffer[1] of "Y","y","Z","z":
- begin loc:=2; err_print('! Where is the matching @@x?');
- @.Where is the match...@>
- end end;
- until (limit>=2) and (buffer[0]="@@")
- and ((buffer[1]="X") or (buffer[1]="x"));
-
- @ Here we are looking at lines following the \.{@@x}.
-
- @<Skip to the next nonblank line...@>=
- repeat incr(ChgLine[ci]);
- if not input_ln(ChgFile[ci]) then
- begin err_print('! Change file #',ci,' ended after @@x');
- @.Change file ended...@>
- return;
- end;
- until limit>0;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2379 m.131 - implement multiple change files
- @ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>=
- begin change_limit:=limit;
- if limit>0 then for k:=0 to limit-1 do change_buffer[k]:=buffer[k];
- end
-
- @ The following procedure 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:
- All of the text down to the \.{@@y} is supposed to match. An error
- message is issued if any discrepancy is found. Then the procedure
- prepares to read the next line from |change_file|.
-
- @p procedure check_change; {switches to |change_file| if the buffers match}
- label exit;
- var n:integer; {the number of discrepancies found}
- @!k:0..buf_size; {index into the buffers}
- begin if lines_dont_match then return;
- n:=0;
- loop@+ begin change_changing; {now it's |true|}
- incr(line);
- if not input_ln(change_file) then
- begin err_print('! Change file ended before @@y');
- @.Change file ended...@>
- change_limit:=0; change_changing; {|false| again}
- return;
- end;
- @<If the current line starts with \.{@@y},
- report any discrepancies and |return|@>;
- @<Move |buffer| and |limit|...@>;
- change_changing; {now it's |false|}
- incr(line);
- if not input_ln(web_file) then
- begin err_print('! WEB file ended during a change');
- @.WEB file ended...@>
- input_has_ended:=true; return;
- end;
- if lines_dont_match then incr(n);
- end;
- exit: end;
- @y
- @ The following procedure is used to see if the next change entries should
- go into effect. The idea is to test whether or not the current
- contents of |buffer| matches the current contents of one |ChgBuffer|.
- If not, there's nothing more to do; but if so, a change is called for:
- All of the text down to the \.{@@y} is supposed to match. An error
- message is issued if any discrepancy is found. Then the procedure
- prepares to read the next line from |ChgFile|.
-
- @p procedure check_change (newch: word);
- {switches to |change_file| if the buffers match}
- label done,exit;
- var n:integer; {the number of discrepancies found}
- @!k:0..buf_size; {index into the buffers}
- SrcFile: word; {this file will be changed by another change file}
- begin if lines_dont_match(newch) then return;
- if ChgLevel=0 then
- SrcFile:= web_file
- else
- SrcFile:= ChgFile[ChgAct[ChgLevel]];
- n:=0;
- incr(ChgLevel); {new change file}
- ChgAct[ChgLevel]:= newch; {store index of new change file}
- loop@+ begin
- incr(ChgLine[newch]);
- if not input_ln(ChgFile[newch]) then
- begin err_print('! Change file #',newch,' ended before @@y');
- @.Change file ended...@>
- ChgLimit[newch]:=0;
- return;
- end;
- @<If the current line starts with \.{@@y},
- report any discrepancies and |return|@>;
- ChgLimit[newch]:= limit;
- move(buffer,ChgBuffer[newch],limit);
- if SrcFile=web_file then
- incr(line)
- else
- incr(ChgLine[ChgAct[ChgLevel-1]]);
-
- loop@+ begin
- if not input_ln(SrcFile) then begin
- if SrcFile=web_file then begin
- err_print('! WEB file ended during a change');
- @.WEB file ended...@>
- input_has_ended:=true; return;
- end else
- @<Remove |ChgAct[ChgLevel-1]|@>
- end else begin
- if (SrcFile=web_file) or (limit<2) or (buffer[0]<>"@@")
- or ((buffer[1]<>"z") and (buffer[1]<>"Z")) then begin
- if SrcFile=web_file then
- incr(line)
- else
- incr(ChgLine[ChgAct[ChgLevel-1]]);
- goto done
- end else begin
- incr(ChgLine[ChgAct[ChgLevel-1]]);
- prime_the_change_buffer(ChgAct[ChgLevel-1]);
- @<Remove |ChgAct[ChgLevel-1]|@>
- end
- end
- end;
- done:
- if lines_dont_match(newch) then incr(n);
- end;
- exit: end;
-
- @ @<Remove |ChgAct[ChgLevel-1]|@>=
- begin decr(ChgLevel);
- ChgAct[ChgLevel]:= ChgAct[ChgLevel+1];
- if ChgLevel=1 then
- SrcFile:= web_file
- else
- SrcFile:= ChgFile[ChgAct[ChgLevel-1]]
- end;
- @z
- 2423
- ────────────────────────────────────────────────────────────────
- @x l.2439 m.134 - implement multiple change files
- @ @<Initialize the input system@>=
- open_input; line:=0; other_line:=0;@/
- changing:=true; prime_the_change_buffer; change_changing;@/
- limit:=0; loc:=1; buffer[0]:=" "; input_has_ended:=false;
- @y
- @ @<Initialize the input system@>=
- open_input; line:=0;@/
- ChgLevel:= ChgCnt;
- while ChgLevel>0 do begin
- ChgLine[ChgLevel]:= 0;
- prime_the_change_buffer(ChgLevel);
- decr(ChgLevel)
- end;
- limit:=0; loc:=1; buffer[0]:=" "; input_has_ended:=false;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2448 m.135 - implement multiple change files
- @p procedure get_line; {inputs the next line}
- label restart;
- begin restart: if changing then
- @<Read from |change_file| and maybe turn off |changing|@>;
- if not changing then
- begin @<Read from |web_file| and maybe turn on |changing|@>;
- if changing then goto restart;
- end;
- loc:=0; buffer[limit]:=" ";
- end;
-
- @ @<Read from |web_file|...@>=
- begin incr(line);
- if not input_ln(web_file) then input_has_ended:=true
- else if limit=change_limit then
- if buffer[0]=change_buffer[0] then
- if change_limit>0 then check_change;
- end
-
- @ @<Read from |change_file|...@>=
- begin incr(line);
- if not input_ln(change_file) then
- begin err_print('! Change file ended without @@z');
- @.Change file ended...@>
- buffer[0]:="@@"; buffer[1]:="z"; limit:=2;
- end;
- if limit>1 then {check if the change has ended}
- if buffer[0]="@@" then
- begin if (buffer[1]>="X")and(buffer[1]<="Z") then
- buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
- if (buffer[1]="x")or(buffer[1]="y") then
- begin loc:=2; err_print('! Where is the matching @@z?');
- @.Where is the match...@>
- end
- else if buffer[1]="z" then
- begin prime_the_change_buffer; change_changing;
- end;
- end;
- end
-
- @ At the end of the program, we will tell the user if the change file
- had a line that didn't match any relevant line in |web_file|.
-
- @<Check that all changes have been read@>=
- if change_limit<>0 then {|changing| is false}
- begin for loc:=0 to change_limit do buffer[loc]:=change_buffer[loc];
- limit:=change_limit; changing:=true; line:=other_line; loc:=change_limit;
- err_print('! Change file entry did not match');
- @.Change file entry did not match@>
- end
- @y
- @p procedure get_line; {inputs the next line}
- label restart,reswitch,continue;
- var
- i,OldLevel: 0..ChangeMax;
- begin restart: @/
- if ChgLevel>0 then
- @<Read the next line from a |ChgFile|@>;
- if ChgLevel=0 then
- begin @<Read from |web_file| and maybe increment |ChgLevel|@>;
- if ChgLevel>0 then goto restart;
- end;
- loc:=0; buffer[limit]:=" ";
- end;
-
- @ @<Read from |web_file|...@>=
- begin incr(line);
- if not input_ln(web_file) then input_has_ended:=true
- else begin
- i:= 0;
- while (i<ChgCnt) and (ChgLevel=0) do begin
- incr(i);
- if (ChgLimit[i]=limit) and (buffer[0]=ChgBuffer[i][0])
- and (ChgLimit[i]>0) then
- check_change(i);
- end
- end
- end
-
- @ @<Read the next...@>=
- begin reswitch:
- incr(ChgLine[ChgAct[ChgLevel]]);
- if not input_ln(ChgFile[ChgAct[ChgLevel]]) then
- begin err_print('! Change file #',ChgAct[ChgLevel],' ended without @@z');
- @.Change file ended...@>
- buffer[0]:="@@"; buffer[1]:="z"; limit:=2;
- end;
- if (limit>1) and (buffer[0]="@@") then {check if the change has ended}
- case buffer[1] of
- "X","x","Y","y":
- begin loc:=2; err_print('! Where is the matching @@z?');
- @.Where is the match...@>
- goto continue;
- end;
- "Z","z":
- begin
- prime_the_change_buffer(ChgAct[ChgLevel]);
- decr(ChgLevel);
- if ChgLevel>0 then goto reswitch; {read from previous CHG file}
- goto continue
- end;
- end; {case}
- i:= ChgAct[ChgLevel]; OldLevel:= ChgLevel;
- while (i<ChgCnt) and (OldLevel=ChgLevel) do begin
- incr(i);
- if (ChgLimit[i]=limit) and (buffer[0]=ChgBuffer[i][0])
- and (ChgLimit[i]>0) then
- check_change(i)
- end;
- continue:
- end
-
- @ At the end of the program, we will tell the user if the change file
- had a line that didn't match any relevant line in |web_file|.
-
- @<Check that all changes have been read@>=
- for ChgLevel:= 1 to ChgCnt do if ChgLimit[ChgLevel]<>0 then
- begin
- move(ChgBuffer[ChgLevel],buffer,ChgLimit[ChgLevel]);
- limit:=ChgLimit[ChgLevel];
- loc:=ChgLimit[ChgLevel];
- err_print('! Change file #',ChgLevel,': entry did not match');
- @.Change file entry did not match@>
- end
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2538 m.140 - implement an inline assembler
- "\": control_code:=force_line; {force a new line in \PASCAL\ output}
- @y
- "\": control_code:=force_line; {force a new line in \PASCAL\ output}
- "[": control_code:= asm_start;
- "]": control_code:= asm_end;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2653 m.146 - make use of Turbo's hex constants
- "A",up_to("Z"),"a",up_to("z"): @<Get an identifier@>;
- @y
- "A".."Z","a".."z","_": @<Get an identifier@>;
- "$": begin c:= hex; scanning_hex:= true end;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2840 m.160
- var accumulator:integer; {accumulates sums}
- @y
- var accumulator:LongInt; {accumulates sums}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2843 m.160
- @!val:integer; {constants being evaluated}
- @y
- @!val:LongInt; {constants being evaluated}
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2872 m.161
- add_in(equiv[q]-@'100000);
- @y
- add_in(LongInt(equiv[q])-@'100000);
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2908 m.165 - make use of Turbo's hex constants
- repeat if next_control>="A" then next_control:=next_control+"0"+10-"A";
- @y
- repeat
- if next_control>="a" then next_control:=next_control+"0"+10-"a"
- else if next_control>="A" then next_control:=next_control+"0"+10-"A";
- @z
- ────────────────────────────────────────────────────────────────
- @x l.2952 m.168
- "#": if t=parametric then a:=param;
- @y
- "#": if (t=parametric)or(t=array_type) then a:=param;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.3096 m.180
- if next_control="(" then
- @y
- begin p:=parametric;
- if next_control="[" then p:=array_type;
- if (p=parametric)and(next_control="(")or
- (p=array_type)and(next_control="[") then
- @z
- ────────────────────────────────────────────────────────────────
- @x l.3100 m.180
- if next_control=")" then
- @y
- if (p=parametric)and(next_control=")")or
- (p=array_type)and(next_control="]") then
- @z
- ────────────────────────────────────────────────────────────────
- @x l.3108 m.180
- begin define_macro(parametric); goto continue;
- @y
- begin define_macro(p); goto continue;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.3112 m.180
- end;
- @y
- end;
- end
- @z
- ────────────────────────────────────────────────────────────────
- @x l.3227 m.188
- @p begin initialize;
- @y
- @p
- var
- ExitSave: Pointer;
- @={$F+}@>
- procedure FinishUp;
- @={$F-}@>
- begin
- if ErrorAddr<>NIL then begin
- write_ln('Internal error #',ExitCode);
- ErrorAddr:= NIL;
- Halt(ExitCode)
- end;
- if string_ptr>256 then begin
- @<Finish off the string pool file@>;
- Close(pool)
- end else
- Erase(pool);
- stat @<Print statistics about memory usage@>;@+tats@;@/
- @<Print the job |history|@>;
- @<Close all Files@>;
- mfree(byte_seg);
- mfree(tok_seg);
- mfree(Pascal_buffer);
- ExitProc:= ExitSave;
- end; { FinishUp }
-
- begin { main }
- ExitSave:= ExitProc;
- ExitProc:= @@FinishUp;
-
- fillchar(firstvar,Ofs(lastvar)-Ofs(firstvar),0);
- if (ParamCount=0) then begin
- print_ln(banner);
- print_ln('Usage: TANGLE <WEB file> [<CHG file1>] [<CHG file2>...]'
- +' [Options]');
- print_ln('Options: /d optimize DIV');
- print_ln(' /m optimize MOD');
- print_ln(' /c include comments in inlines');
- print_ln('');
- Halt(error_message)
- end;
- initialize;
- @z
- ────────────────────────────────────────────────────────────────
- @x l.3233 m.188
- end_of_TANGLE:
- if string_ptr>256 then @<Finish off the string pool file@>;
- stat @<Print statistics about memory usage@>;@+tats@;@/
- @t\4\4@>{here files should be closed if the operating system requires it}
- @<Print the job |history|@>;
- @y
- @z
- -- 3286
- ────────────────────────────────────────────────────────────────
- @x l.3293 m.194
- itself will get a new module number.
- @^system dependencies@>
- @y
- itself will get a new module number.
- @^system dependencies@>
-
- Here we add the more extensive changes for this \.{TP} version
- of \.{TANGLE}.
-
- @ First we need an extra module to copy the parameter of an |array_type|
- macro.
-
- @<Copy the array parameter...@>=
- bal:= 1; incr(cur_byte); {skip the opening '[' }
- repeat b:=tok_mem[zo,cur_byte]; incr(cur_byte);
- if b=param then store_two_bytes(word(name_ptr)+@'77777)
- else begin if b>=@'200 then
- begin app_repl(b);
- b:=tok_mem[zo,cur_byte]; incr(cur_byte);
- end
- else case b of
- "[": incr(bal);
- "]": decr(bal);
- { ",": if bal=1 then begin
- decr(cur_byte);
- tok_mem[zo,cur_byte]:="[";
- bal:= 0
- end;
- }
- "'": repeat app_repl(b);
- b:=tok_mem[zo,cur_byte]; incr(cur_byte);
- until b="'"; {copy string, don't change |bal|}
- othercases do_nothing
- endcases;
- if bal<>0 then app_repl(b)
- end
- until bal=0
-
- @ The following function returns a commandline parameter without
- an option
-
- @<All purpose procedures and functions@>=
- function Parameter (i: word): String;
- var
- p: word;
- s: String;
- begin
- s:= ParamStr(i);
- p:= pos('-',s);
- if p=0 then p:= pos('/',s);
- if p>0 then byte(s[0]):= p-1;
- Parameter:= s
- end;
- @z