@x \def\title{TANGLE} @y \def\title{MANGLE} @z @x \def\contentspagenumber{123} % should be odd @y \def\contentspagenumber{1} % should be odd @z @x \centerline{\titlefont The {\ttitlefont TANGLE} processor} @y \centerline{\titlefont The {\ttitlefont MANGLE} processor} @z @x \vfill} \pageno=\contentspagenumber \advance\pageno by 1 @y \centerline{(Microsoft Pascal Version 2.8.0, July 1986)} \vfill \centerline{\hsize 5in\baselineskip9pt \vbox{\ninerm\noindent The changes required to create this Microsoft Pascal version of \.{MANGLE} are the portions of the sections marked with asterisks (*) below.}}} \pageno=1 @z @x @d banner=='This is TANGLE, Version 2.8' @y For the Microsoft Pascal version of \.{MANGLE}, we add a second decimal point and number to the main version number, so we can differentiate between changes to the standard \.{TANGLE}\ and changes local to the Microsoft Pascal version of \.{MANGLE}. @d banner=='This is MANGLE, Microsoft Pascal Version 2.8.0' @z @x @@/ procedure initialize; var @@/ begin @@/ @y @@/ @@/ procedure initialize; var @@/ begin @@/ @@/ @z @x @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== {change this to `$\\{stat}\equiv\null$' when gathering usage statistics} @d tats== {change this to `$\\{tats}\equiv\null$' when gathering usage statistics} @z @x @{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead} @!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging} @y @{@&$Debug-@} {no range check, catch arithmetic overflow, no debug overhead} @z @x @d othercases == others: {default for cases not listed explicitly} @y @d othercases == otherwise {default for cases not listed explicitly} @z @x @!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; @y @!max_bytes=9000; {|1/ww| times the number of bytes in identifiers, strings, and module names; must be less than 65536} @!max_toks=16000; {|1/zz| times the number of bytes in compressed \PASCAL\ code; @z @x @!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=31; {long identifiers are chopped to this length, which must not exceed |line_length|} @!unambig_length=20; {identifiers must be unique if chopped to this length} @z @x @d last_text_char=127 {ordinal number of the largest element of |text_char|} @= @!text_file=packed file of text_char; @y @d last_text_char=127 {ordinal number of the largest element of |text_char|} @d text_file==text @z @x rewrite(term_out,'TTY:'); {send |term_out| output to the terminal} @y assign(term_out,'con:'); rewrite(term_out); {send |term_out| output to the terminal} @z @x @d update_terminal == break(term_out) {empty the terminal output buffer} @y @d update_terminal == @z @x rewrite(Pascal_file); rewrite(pool); @y setup_text_file(web_file,'web','web',true); setup_text_file(change_file,'ch','change',false); setup_text_file(Pascal_file,'pas','Pascal',false); setup_text_file(pool,'pol','string pool',false); rewrite(Pascal_file); rewrite(pool); @z @x or |carriage_return|. @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} 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 or |carriage_return|. Since the inner loop of |input_ln| is part of \.{TANGLE}'s ``inner loop''---each character of input comes in at this place---it is wise to reduce system overhead by making use of special routines that read in an entire array of characters at once. Microsoft Pascal allows us to use a `|lstring|' to read an entire line from the file. @^system dependencies@> @^inner loop@> @p function input_ln(var f:text_file):boolean; {inputs a line or returns |false|} label done; var @!aux_buf:lstring(buf_size); {intermediate input buffer} k:0..buf_size; {index into |buffer|} begin limit:=0; if eof(f) then input_ln:=false else begin read(f,aux_buf); read_ln(f); limit:=ord(aux_buf.len); if limit>=buf_size then begin print_nl('! Input line too long'); loc:=0; error; @.Input line too long@> limit:=buf_size-1; end; for k:=1 to limit do buffer[k-1]:=xord[aux_buf[k]]; while limit>1 do if buffer[limit-1]=" " then decr(limit) @+else goto done; done: input_ln:=true; end; end; @z @x and jumps out of the program. This is the only non-local |goto| statement in \.{TANGLE}. It is used when no recovery from a particular error has been provided. @y and jumps out of the program. It calls a DOS routine to exit. It is used when no recovery from a particular error has been provided. @z @x begin goto end_of_TANGLE; end; @y begin stat @;@+tats@;@/ @t\4\4@>{here files should be closed if the operating system requires it} @; endxqq; end; @z @x @!eight_bits=0..255; {unsigned one-byte quantity} @!sixteen_bits=0..65535; {unsigned two-byte quantity} @y @!eight_bits=0..255; {unsigned one-byte quantity} @!sixteen_bits=0..32767; {unsigned two-byte quantity} @!word_sixteen_bits=wrd(0)..65535; {unsigned two-byte quantity} @z @x @d ww=2 {we multiply the byte capacity by approximately this amount} @d zz=3 {we multiply the token capacity by approximately this amount} @= @!byte_mem: packed array [0..ww-1,0..max_bytes] of ASCII_code; {characters of names} @!tok_mem: packed array [0..zz-1,0..max_toks] of eight_bits; {tokens} @!byte_start: array [0..max_names] of sixteen_bits; {directory into |byte_mem|} @!tok_start: array [0..max_texts] of sixteen_bits; {directory into |tok_mem|} @!link: array [0..max_names] of sixteen_bits; {hash table or tree links} @!ilk: array [0..max_names] of sixteen_bits; {type codes or tree links} @!equiv: array [0..max_names] of sixteen_bits; {info corresponding to names} @!text_link: array [0..max_texts] of sixteen_bits; {relates replacement texts} @y @d ww=8 {we multiply the byte capacity by approximately this amount} @d zz=8 {we multiply the token capacity by approximately this amount} @d byte_start==addr_byte_start^ @d tok_start==addr_tok_start^ @d link==addr_link^ @d ilk==addr_ilk^ @d equiv==addr_equiv^ @d text_link==addr_text_link^ @= @!byte_mem: array [0..ww-1] of ads of packed array [0..max_bytes] of ASCII_code; {characters of names} @!tok_mem: array [0..zz-1] of ads of packed array [0..max_toks] of eight_bits; {tokens} @!addr_byte_start: ads of packed array [0..max_names] of sixteen_bits; {directory into |byte_mem|} @!addr_tok_start: ads of packed array [0..max_texts] of sixteen_bits; {directory into |tok_mem|} @!addr_link: ads of packed array [0..max_names] of sixteen_bits; {hash table or tree links} @!addr_ilk: ads of packed array [0..max_names] of sixteen_bits; {type codes or tree links} @!addr_equiv: ads of packed array [0..max_names] of word_sixteen_bits; {info corresponding to names} @!addr_text_link: ads of packed array [0..max_texts] of sixteen_bits; {relates replacement texts} @z @x |ww=2| the even-numbered name bytes appear in |byte_mem[0,@t$*$@>]| and the odd-numbered ones appear in |byte_mem[1,@t$*$@>]|. @y |ww=2| the even-numbered name bytes appear in |byte_mem[0]^[@t$*$@>]| and the odd-numbered ones appear in |byte_mem[1]^[@t$*$@>]|. @z @x |byte_mem[w,@t$*$@>]| is called |byte_ptr[w]|. @y |byte_mem[w]^[@t$*$@>]| is called |byte_ptr[w]|. @z @x @!pool_check_sum:integer; {sort of a hash for the whole string pool} @y @!pool_check_sum:long_integer; {sort of a hash for the whole string pool} @z @x even-numbered replacement texts appear in |tok_mem[0,@t$*$@>]| and the odd-numbered ones appear in |tok_mem[1,@t$*$@>]|. Furthermore, @y even-numbered replacement texts appear in |tok_mem[0]^[@t$*$@>]| and the odd-numbered ones appear in |tok_mem[1]^[@t$*$@>]|. Furthermore, @z @x The first position of |tok_mem[z,@t$*$@>]| that is unoccupied by @y The first position of |tok_mem[z]^[@t$*$@>]| that is unoccupied by @z @x for k:=byte_start[p] to byte_start[p+ww]-1 do print(xchr[byte_mem[w,k]]); @y for k:=byte_start[p] to byte_start[p+ww]-1 do print(xchr[byte_mem[w]^[k]]); @z @x while (ip do q:=equiv[q]; @y if q=p then chop_hash[h]:=ord(equiv[p]) else begin while equiv[q]<>wrd(p) do q:=ord(equiv[q]); @z @x begin byte_mem[w,k]:=buffer[i]; incr(k); incr(i); @y begin byte_mem[w]^[k]:=buffer[i]; incr(k); incr(i); @z @x q:=equiv[q]; @y q:=ord(equiv[q]); @z @x equiv[p]:=chop_hash[h]; chop_hash[h]:=p; {put |p| at front of secondary list} @y equiv[p]:=wrd(chop_hash[h]); chop_hash[h]:=p; {put |p| at front of secondary list} @z @x begin c:=byte_mem[w,k]; @y begin c:=byte_mem[w]^[k]; @z @x for k:=byte_start[q] to byte_start[q+ww]-1 do print(xchr[byte_mem[w,k]]); @y for k:=byte_start[q] to byte_start[q+ww]-1 do print(xchr[byte_mem[w]^[k]]); @z @x equiv[p]:=buffer[id_first+1]+@'100000 else begin equiv[p]:=string_ptr+@'100000; @y equiv[p]:=wrd(buffer[id_first+1])+@'100000 else begin equiv[p]:=wrd(string_ptr)+@'100000; @z @x for j:=1 to l do byte_mem[w,k+j-1]:=mod_text[j]; @y for j:=1 to l do byte_mem[w]^[k+j-1]:=mod_text[j]; @z @x while (k"(") then @y if (stack_ptr=0)or(tok_mem[zo]^[cur_byte]<>"(") then @z @x equiv[name_ptr]:=text_ptr; ilk[name_ptr]:=simple; w:=name_ptr mod ww; @y equiv[name_ptr]:=wrd(text_ptr); ilk[name_ptr]:=simple; w:=name_ptr mod ww; @z @x byte_mem[w,k]:="#"; incr(k); byte_ptr[w]:=k; @y byte_mem[w]^[k]:="#"; incr(k); byte_ptr[w]:=k; @z @x tok_mem[z,tok_ptr[z]]:=#; incr(tok_ptr[z]); end @y tok_mem[z]^[tok_ptr[z]]:=#; incr(tok_ptr[z]); end @z @x loop@+ begin b:=tok_mem[zo,cur_byte]; incr(cur_byte); @y loop@+ begin b:=tok_mem[zo]^[cur_byte]; incr(cur_byte); @z @x if b=param then store_two_bytes(name_ptr+@'77777) @y if b=param then store_two_bytes(wrd(name_ptr)+@'77777) @z @x b:=tok_mem[zo,cur_byte]; incr(cur_byte); @y b:=tok_mem[zo]^[cur_byte]; incr(cur_byte); @z @x b:=tok_mem[zo,cur_byte]; incr(cur_byte); @y b:=tok_mem[zo]^[cur_byte]; incr(cur_byte); @z @x @!out_val,@!out_app:integer; {pending values} @y @!out_val,@!out_app:long_integer; {pending values} @z @x @d check_break==if out_ptr>line_length then flush_buffer @p procedure flush_buffer; {writes one line to output file} var k:0..out_buf_size; {index into |out_buf|} @!b:0..out_buf_size; {value of |break_ptr| upon entry} begin b:=break_ptr; if (semi_ptr<>0)and(out_ptr-semi_ptr<=line_length) then break_ptr:=semi_ptr; for k:=1 to break_ptr do write(Pascal_file,xchr[out_buf[k-1]]); write_ln(Pascal_file); incr(line); @y Microsoft Pascal allows us to use a `|lstring|' to write an entire line at once, rather than character by character. @d check_break==if out_ptr>line_length then flush_buffer @p procedure flush_buffer; {writes one line to output file} var k:0..out_buf_size; {index into |out_buf|} @!b:0..out_buf_size; {value of |break_ptr| upon entry} @!aux_buf:lstring(out_buf_size); {intermediate input buffer} begin b:=break_ptr; if (semi_ptr<>0)and(out_ptr-semi_ptr<=line_length) then break_ptr:=semi_ptr; for k:=1 to break_ptr do aux_buf[k]:=xchr[out_buf[k-1]]; aux_buf.len:=wrd(break_ptr); write_ln(Pascal_file,aux_buf); incr(line); @z @x @p procedure app_val(@!v:integer); {puts |v| into buffer, assumes |v>=0|} @y @p procedure app_val(@!v:long_integer); {puts |v| into buffer, assumes |v>=0|} @z @x repeat out_buf[k]:=v mod 10; v:=v div 10; decr(k); @y repeat out_buf[k]:=ord(v mod 10); v:=v div 10; decr(k); @z @x sign: begin app(","-out_app); check_break; break_ptr:=out_ptr; @y sign: begin app(ord(","-out_app)); check_break; break_ptr:=out_ptr; @z @x last_sign:=out_app; @y last_sign:=ord(out_app); @z @x @p procedure send_val(@!v:integer); {output the (signed) value |v|} @y @p procedure send_val(@!v:long_integer); {output the (signed) value |v|} @z @x @!n:integer; {number being scanned} @y @!n:long_integer; {number being scanned} @z @x @= "!","""","#","$","%","&","(",")","*",",","/",":",";","<","=",">","?", "@@","[","\","]","^","_","`","{","|" @y @= "!","""","#","$","%","&","(",")","*",",","/",":",";","<","=",">","?", "@@","[","\","]","^","_","`","{","}","|","~" @z @x identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww; while (k @= @!long_integer=@=integer4@>;@/ @ Various procedures. @^system dependencies@> @= @+function getmqq(l : word) :adsmem; extern; @+function memavl : word ; extern; @+procedure endxqq ; extern; @ Here we define some procedures to get the lengths of the various blocks. @= function len_tok_mem :word; var @!dummy_tok_mem: packed array [0..max_toks] of eight_bits; {tokens} begin len_tok_mem := sizeof(dummy_tok_mem); end; function len_byte_mem :word; var @!dummy_byte_mem : packed array [0..max_bytes] of ASCII_code; begin len_byte_mem := sizeof(dummy_byte_mem); end; function len_names :word; var @!dummy_names: packed array [0..max_names] of sixteen_bits; {directory into |byte_mem|} begin len_names := sizeof(dummy_names); end; function len_texts :word; var @!dummy_texts: packed array [0..max_texts] of sixteen_bits; {directory into |tok_mem|} begin len_texts := sizeof(dummy_texts); end; @ We define some temporary variables to hold the lengths of the various block types. @^system dependencies@> @= @!len_of_names : word; @!len_of_texts : word; @!len_of_byte_mem : word; @!len_of_tok_mem : word; @ Here we actually allocate the various buffers used by the program. @^system dependencies@> @d allocate_name_sized_buffer(#) == # := getmqq(len_of_names) @d allocate_text_sized_buffer(#) == # := getmqq(len_of_texts) @= len_of_names := len_names; len_of_texts := len_texts; len_of_byte_mem := len_byte_mem; len_of_tok_mem := len_tok_mem; allocate_name_sized_buffer(addr_byte_start); allocate_name_sized_buffer(addr_link); allocate_name_sized_buffer(addr_ilk); allocate_name_sized_buffer(addr_equiv); allocate_text_sized_buffer(addr_tok_start); allocate_text_sized_buffer(addr_text_link); for wi:=0 to ww-1 do byte_mem[wi] := getmqq(len_of_byte_mem); for zi:=0 to zz-1 do tok_mem[zi] := getmqq(len_of_tok_mem); @ @^system dependencies@> @= doseqq [extern] : word; {completion code to be returned to DOS} @ This procedure appends a default file extension to a file name. @^system dependencies@> @= @+procedure setup_text_file(var the_file : text_file; @+const def_ext :lstring; @+const descr_str : lstring; main : boolean); extern; @z