Created
September 3, 2019 13:03
-
-
Save texdraft/6392d089c6bfd626bb20824b997073d6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
program tangle(web_file, change_file, Pascal_file, pool); | |
label | |
end_of_TANGLE; | |
const | |
buf_size = 100; | |
max_bytes = 45000; | |
max_toks = 50000; | |
max_names = 4000; | |
max_texts = 2000; | |
hash_size = 353; | |
longest_name = 400; | |
line_length = 72; | |
out_buf_size = 144; | |
stack_size = 50; | |
max_id_length = 12; | |
unambig_length = 7; | |
type | |
ASCII_code = 0..255; | |
text_file = packed file of char; | |
eight_bits = 0..255; | |
sixteen_bits = 0..65535; | |
name_pointer = 0..max_names; | |
text_pointer = 0..max_texts; | |
output_state = record | |
end_field: sixteen_bits; {ending location of replacement text} | |
byte_field: sixteen_bits; {present location within replacement text} | |
name_field: name_pointer; {|byte_start| index for text being output} | |
repl_field: text_pointer; {|tok_start| index for text being output} | |
mod_field: 0..027777; {module number or zero if not a module} | |
end; | |
var | |
history: spotless..fatal_message; {how bad was this run?} | |
xord: array[char] of ASCII_code; {specifies conversion of input characters} | |
xchr: array[ASCII_code] of char; {specifies conversion of output characters} | |
term_out: text_file; {the terminal as an output file} | |
web_file: text_file; {primary input} | |
change_file: text_file; {updates} | |
Pascal_file: text_file; | |
pool: text_file; | |
buffer: array[0..buf_size] of ASCII_code; | |
phase_one: boolean; {|true| in Phase I, |false| in Phase II} | |
byte_mem: packed array[0..1, 0..max_bytes] of ASCII_code; {characters of names} | |
tok_mem: packed array[0..2, 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} | |
name_ptr: name_pointer; {first unused position in |byte_start|} | |
string_ptr: name_pointer; {next number to be given to a string of length | <> 1|} | |
byte_ptr: array[0..1] of 0..max_bytes; {first unused position in |byte_mem|} | |
pool_check_sum: integer; {sort of a hash for the whole string pool} | |
text_ptr: text_pointer; {first unused position in |tok_start|} | |
tok_ptr: array[0..2] of 0..max_toks; {first unused position in a given segment of |tok_mem|} | |
z: 0..2; {current segment of |tok_mem|} | |
id_first: 0..buf_size; {where the current identifier begins in the buffer} | |
id_loc: 0..buf_size; {just after the current identifier in the buffer} | |
double_chars: 0..buf_size; {correction to length in case of strings} | |
hash, chop_hash: array[0..hash_size] of sixteen_bits; {heads of hash lists} | |
chopped_id: array[0..unambig_length] of ASCII_code; {chopped identifier} | |
mod_text: array[0..longest_name] of ASCII_code; {name being sought for} | |
xxxxx: integer; | |
last_unnamed: text_pointer; {most recent replacement text of unnamed module} | |
cur_state: output_state; {|cur_end|, |cur_byte|, |cur_name|, |cur_repl|, |cur_mod|} | |
stack: array[1..stack_size] of output_state; {info for non - current levels} | |
stack_ptr: 0..stack_size; {first unused location in the output state stack} | |
zo: 0..2; {the segment of |tok_mem| from which output is coming} | |
brace_level: eight_bits; (*current depth of $\.{@@\{}\ldots\.{@@\}}$ nesting*) | |
cur_val: integer; {additional information corresponding to output token} | |
out_buf: array[0..out_buf_size] of ASCII_code; {assembled characters} | |
out_ptr: 0..out_buf_size; {first available place in |out_buf|} | |
break_ptr: 0..out_buf_size; {last breaking place in |out_buf|} | |
semi_ptr: 0..out_buf_size; {last semicolon breaking place in |out_buf|} | |
out_state: eight_bits; {current status of partial output} | |
out_val, out_app: integer; {pending values} | |
out_sign: ASCII_code; {sign to use if appending |out_val >= 0|} | |
last_sign: - 1.. + 1; {sign to use if appending a zero} | |
out_contrib: array[1..line_length] of ASCII_code; {a contribution to |out_buf|} | |
ii: integer; {general purpose |for| loop variable in the outer block} | |
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|} | |
change_buffer: array[0..buf_size] of ASCII_code; | |
change_limit: 0..buf_size; {the last position occupied in |change_buffer|} | |
cur_mod: name_pointer; {name of module just scanned} | |
scanning_hex: boolean; {are we scanning a hexadecimal constant?} | |
next_control: eight_bits; {control code waiting to be acted upon} | |
cur_repl_text: text_pointer; {replacement text formed by |scan_repl|} | |
module_count: 0..027777; {the current module number} | |
procedure error; {prints '\..' and location of error message} | |
var | |
j: 0..out_buf_size; {index into |out_buf|} | |
k, l: 0..buf_size; {indices into |buffer|} | |
begin | |
if phase_one then | |
begin | |
if changing then | |
print('. (change file ') | |
else | |
print('. ('); | |
print_ln('l.', line:1, ')'); | |
if loc >= limit then | |
l := limit | |
else | |
l := loc; | |
for k := 1 to l do | |
if buffer[k - 1] = tab_mark then | |
print(' ') | |
else | |
print(xchr[buffer[k - 1]]); {print the characters already read} | |
new_line; | |
for k := 1 to l do | |
print(' '); {space out the next line} | |
for k := l + 1 to limit do | |
print(xchr[buffer[k - 1]]); {print the part not yet read} | |
print(' '); {this space separates the message from future asterisks} | |
end | |
else | |
begin | |
print_ln('. (l.', line:1, ')'); | |
for j := 1 to out_ptr do | |
print(xchr[out_buf[j - 1]]); {print current partial line} | |
print('... '); {indicate that this information is partial} | |
end; | |
update_terminal; | |
mark_error; | |
end; | |
procedure jumpout; | |
begin | |
goto end_of_TANGLE; | |
end; | |
procedure initialize; | |
var | |
i: 0..255; | |
wi: 0..1; {to initialize the |byte_mem| indices} | |
zi: 0..2; | |
h: 0..hash_size; {index into hash-head arrays} | |
begin | |
history := spotless; | |
xchr[32] := ' '; | |
xchr[33] := '!'; | |
xchr[34] := '"'; | |
xchr[35] := '#'; | |
xchr[36] := '$'; | |
xchr[37] := '%'; | |
xchr[38] := '&'; | |
xchr[39] := ''''; | |
xchr[40] := '('; | |
xchr[41] := ')'; | |
xchr[42] := '*'; | |
xchr[43] := ' + '; | |
xchr[44] := ', '; | |
xchr[45] := ' - '; | |
xchr[46] := '.'; | |
xchr[47] := '/'; | |
xchr[48] := '0'; | |
xchr[49] := '1'; | |
xchr[50] := '2'; | |
xchr[51] := '3'; | |
xchr[52] := '4'; | |
xchr[53] := '5'; | |
xchr[54] := '6'; | |
xchr[55] := '7'; | |
xchr[56] := '8'; | |
xchr[57] := '9'; | |
xchr[58] := ':'; | |
xchr[59] := ';'; | |
xchr[60] := '<'; | |
xchr[61] := ' = '; | |
xchr[62] := ' > '; | |
xchr[63] := '?'; | |
xchr[64] := '@'; | |
xchr[65] := 'A'; | |
xchr[66] := 'B'; | |
xchr[67] := 'C'; | |
xchr[68] := 'D'; | |
xchr[69] := 'E'; | |
xchr[70] := 'F'; | |
xchr[71] := 'G'; | |
xchr[72] := 'H'; | |
xchr[73] := 'I'; | |
xchr[74] := 'J'; | |
xchr[75] := 'K'; | |
xchr[76] := 'L'; | |
xchr[77] := 'M'; | |
xchr[78] := 'N'; | |
xchr[79] := 'O'; | |
xchr[80] := 'P'; | |
xchr[81] := 'Q'; | |
xchr[82] := 'R'; | |
xchr[83] := 'S'; | |
xchr[84] := 'T'; | |
xchr[85] := 'U'; | |
xchr[86] := 'V'; | |
xchr[87] := 'W'; | |
xchr[88] := 'X'; | |
xchr[89] := 'Y'; | |
xchr[90] := 'Z'; | |
xchr[91] := '['; | |
xchr[92] := '\'; | |
xchr[93] := ']'; | |
xchr[94] := '^'; | |
xchr[95] := '_'; | |
xchr[96] := '`'; | |
xchr[97] := 'a'; | |
xchr[98] := 'b'; | |
xchr[99] := 'c'; | |
xchr[100] := 'd'; | |
xchr[101] := 'e'; | |
xchr[102] := 'f'; | |
xchr[103] := 'g'; | |
xchr[104] := 'h'; | |
xchr[105] := 'i'; | |
xchr[106] := 'j'; | |
xchr[107] := 'k'; | |
xchr[108] := 'l'; | |
xchr[109] := 'm'; | |
xchr[110] := 'n'; | |
xchr[111] := 'o'; | |
xchr[112] := 'p'; | |
xchr[113] := 'q'; | |
xchr[114] := 'r'; | |
xchr[115] := 's'; | |
xchr[116] := 't'; | |
xchr[117] := 'u'; | |
xchr[118] := 'v'; | |
xchr[119] := 'w'; | |
xchr[120] := 'x'; | |
xchr[121] := 'y'; | |
xchr[122] := 'z'; | |
xchr[123] := '{'; | |
xchr[124] := '|'; | |
xchr[125] := '}'; | |
xchr[126] := '~'; | |
xchr[0] := ' '; | |
xchr[127] := ' '; | |
for i := 1 to 037 do | |
xchr[i] := ' '; | |
for i := 0200 to 0377 do | |
xchr[i] := ' '; | |
for i := first_text_char to last_text_char do | |
xord[chr(i)] := " "; | |
for i := 1 to 0377 do | |
xord[xchr[i]] := i; | |
xord[' '] := " "; | |
rewrite(term_out, 'tty:'); | |
rewrite(Pascal_file); | |
rewrite(pool); | |
for wi := 0 to ww - 1 do | |
begin | |
byte_start[wi] := 0; | |
byte_ptr[wi] := 0; | |
end; | |
byte_start[2] := 0; | |
name_ptr := 1; | |
string_ptr := 256; | |
pool_check_sum := 271828; | |
for zi := 0 to 2 do | |
begin | |
tok_start[zi] := 0; | |
tok_ptr[zi] := 0; | |
end; | |
tok_start[3] := 0; | |
text_ptr := 1; | |
z := 1 mod 3; | |
ilk[0] := 0; | |
equiv[0] := 0; | |
for h := 0 to hash_size - 1 do | |
begin | |
hash[h] := 0; | |
chop_hash[h] := 0; | |
end; | |
xxxxx := 0; | |
last_unnamed := 0; | |
text_link[0] := 0; | |
scanning_hex := false; | |
mod_text[0] := 32; | |
end; | |
procedure open_input; | |
begin | |
reset(web_file); | |
reset(change_file); | |
end; | |
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); | |
limit := limit - 1; | |
if final_limit > limit then | |
final_limit := limit; | |
print_nl('! Input line too long'); | |
loc := 0; | |
error; | |
end; | |
end; | |
read_ln(f); | |
limit := final_limit; | |
input_ln := true; | |
end; | |
end; | |
procedure print_id(p:name_pointer); {print identifier or module name} | |
var | |
k: 0..max_bytes; {index into |byte_mem|} | |
w: 0..1; {segment of |byte_mem|} | |
begin | |
if p >= name_ptr then | |
print('IMPOSSIBLE') | |
else | |
begin | |
w := p mod ww; | |
for k := byte_start[p] to byte_start[p + ww] - 1 do | |
print(xchr[byte_mem[w, k]]); | |
end; | |
end; | |
function id_lookup(t: eight_bits): name_pointer; | |
label | |
found, not_found; | |
var | |
c: eight_bits; {byte being chopped} | |
i: 0..buf_size; {index into |buffer|} | |
h: 0..hash_size; {hash code} | |
k: 0..max_bytes; {index into |byte_mem|} | |
w: 0..1; {segment of |byte_mem|} | |
l: 0..buf_size; {length of the given identifier} | |
p, q: name_pointer; {where the identifier is being sought} | |
s: 0..unambig_length; {index into |chopped_id|} | |
begin | |
l := id_loc - id_first; {compute the length} | |
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; | |
p := hash[h]; | |
while p <> 0 do | |
begin | |
if length(p) = l then | |
begin | |
i := id_first; | |
k := byte_start[p]; | |
w := p mod ww; | |
while (i < id_loc) and (buffer[i] = byte_mem[w, k]) do | |
begin | |
incr(i); | |
incr(k); | |
end; | |
if i = id_loc then | |
goto found; {all characters agree} | |
end; | |
p := link[p]; | |
end; | |
p := name_ptr; {the current identifier is new} | |
link[p] := hash[h]; {insert |p| at beginning of hash list} | |
hash[h] := p; | |
found:; | |
if (p = name_ptr) or (t <> 0)then | |
begin | |
if ((p <> name_ptr) and (t <> normal) and (ilk[p] = normal)) or ((p = name_ptr) and | |
(t = normal) and (buffer[id_first] <> """")) then | |
begin | |
i := id_first; | |
s := 0; | |
h := 0; | |
while (i < id_loc) and (s < unambig_length) do | |
begin | |
if buffer[i] <> "_" then | |
begin | |
if buffer[i] >= "a" then | |
chopped_id[s] := buffer[i] - 040 | |
else | |
chopped_id[s] := buffer[i]; | |
h := (h + h + chopped_id[s]) mod hash_size; | |
incr(s); | |
end; | |
incr(i); | |
end; | |
chopped_id[s] := 0; | |
end; | |
if p <> name_ptr then | |
begin {now |p <> name_ptr| and |t <> normal|} | |
if ilk[p] = normal then | |
begin | |
if t = numeric then | |
err_print('! This identifier has already appeared'); | |
q := chop_hash[h]; | |
if q = p then | |
chop_hash[h] := equiv[p] | |
else | |
begin | |
while equiv[q] <> p do | |
q := equiv[q]; | |
equiv[q] := equiv[p]; | |
end; | |
end | |
else | |
err_print('! This identifier was defined before'); | |
ilk[p] := t; | |
end | |
else | |
begin | |
if (t = normal) and (buffer[id_first] <> """")then | |
begin | |
q := chop_hash[h]; | |
while q <> 0 do | |
begin | |
begin | |
k := byte_start[q]; | |
s := 0; | |
w := q mod ww; | |
while (k < byte_start[q + ww]) and (s < unambig_length) do | |
begin | |
c := byte_mem[w, k]; | |
if c <> "_" then | |
begin | |
if c >= "a" then | |
c := c - 040; {merge lowercase with uppercase} | |
if chopped_id[s] <> c then | |
goto not_found; | |
incr(s); | |
end; | |
incr(k); | |
end; | |
if (k = byte_start[q + ww]) and (chopped_id[s] <> 0) then | |
goto not_found; | |
print_nl('! identifier conflict with '); | |
for k := byte_start[q] to byte_start[q + ww] - 1 do | |
print(xchr[byte_mem[w, k]]); | |
error; | |
q := 0; {only one conflict will be printed, since |equiv[0]=0|} | |
not_found: | |
end; | |
q := equiv[q]; | |
end; | |
equiv[p] := chop_hash[h]; | |
chop_hash[h] := p; | |
end; | |
w := name_ptr mod ww; | |
k := byte_ptr[w]; | |
if k + l > max_bytes then | |
overflow('byte memory'); | |
if name_ptr > max_names - ww then | |
overflow('name'); | |
i := id_first; {get ready to move the identifier into |byte_mem|} | |
while i < id_loc do | |
begin | |
byte_mem[w, k] := buffer[i]; | |
incr(k); | |
incr(i); | |
end; | |
byte_ptr[w] := k; | |
byte_start[name_ptr + ww] := k; | |
incr(name_ptr); | |
if buffer[id_first] <> """" then | |
ilk[p] := t | |
else | |
begin | |
ilk[p] := numeric; {strings are like numeric macros} | |
if l - double_chars = 2 then {this string is for a single character} | |
equiv[p] := buffer[id_first + 1] + 0100000 | |
else | |
begin | |
equiv[p] := string_ptr + 0100000; | |
l := l - double_chars - 1; | |
if l > 99 then | |
err_print('! Preprocessed string is too long'); | |
incr(string_ptr); | |
write(pool, xchr["0" + l div 10], xchr["0" + l mod 10]); {output the length} | |
pool_check_sum := pool_check_sum + pool_check_sum + l; | |
while pool_check_sum > check_sum_prime do | |
pool_check_sum := pool_check_sum - check_sum_prime; | |
i := id_first + 1; | |
while i < id_loc do | |
begin | |
write(pool, xchr[buffer[i]]); {output characters of string} | |
pool_check_sum := pool_check_sum + pool_check_sum + buffer[i]; | |
while pool_check_sum > check_sum_prime do | |
pool_check_sum := pool_check_sum - check_sum_prime; | |
if (buffer[i] = """") or (buffer[i] = "@") then | |
i := i + 2 {omit second appearance of doubled character} | |
else | |
incr(i); | |
end; | |
write_ln(pool); | |
end; | |
end; | |
end; | |
end; | |
id_lookup := p; | |
end; | |
function mod_lookup(l: sixteen_bits): name_pointer; | |
label | |
found; | |
var | |
c: less..extension; {comparison between two names} | |
j: 0..longest_name; {index into |mod_text|} | |
k: 0..max_bytes; {index into |byte_mem|} | |
w: 0..ww - 1; {segment of |byte_mem|} | |
p: name_pointer; {current node of the search tree} | |
q: name_pointer; {father of node |p|} | |
begin | |
c := greater; | |
q := 0; | |
p := rlink[0]; {|rlink[0]| is the root of the tree} | |
while p <> 0 do | |
begin | |
begin | |
k := byte_start[p]; | |
w := p mod ww; | |
c := equal; | |
j := 1; | |
while (k < byte_start[p + ww]) and (j< = l) and (mod_text[j] = byte_mem[w, k]) do | |
begin | |
incr(k); | |
incr(j); | |
end; | |
if k = byte_start[p + ww] then | |
if j > l then | |
c := equal | |
else | |
c := extension | |
else | |
if j > l then | |
c := equal | |
else | |
if mod_text[j]<byte_mem[w, k] then | |
c := prefix | |
else c := greater; | |
end; | |
q := p; | |
if c = less then | |
p := llink[q] | |
else | |
if c = greater then | |
p := rlink[q] | |
else | |
goto found; | |
end; | |
w := name_ptr mod ww; | |
k := byte_ptr[w]; | |
if k + l > max_bytes then | |
overflow('byte memory'); | |
if name_ptr > max_names - ww then | |
overflow('name'); | |
p := name_ptr; | |
if c = less then | |
llink[q] := p | |
else | |
rlink[q] := p; | |
llink[p] := 0; | |
rlink[p] := 0; | |
c := equal; | |
equiv[p] := 0; | |
for j := 1 to l do | |
byte_mem[w, k + j - 1] := mod_text[j]; | |
byte_ptr[w] := k + l; | |
byte_start[name_ptr + ww] := k + l; | |
incr(name_ptr); | |
found: | |
if c <> equal then | |
begin | |
err_print('! Incompatible section names'); | |
p := 0; | |
end; | |
print_id(p); | |
new_line; | |
xxxxx := xxxxx + 1; | |
print_ln(xxxxx); | |
mod_lookup := p; | |
end; | |
function prefix_lookup(l: sixteen_bits): name_pointer; {finds name extension} | |
var | |
c: less..extension; {comparison between two names} | |
count: 0..max_names; {the number of hits} | |
j: 0..longest_name; {index into |mod_text|} | |
k: 0..max_bytes; {index into |byte_mem|} | |
w: 0..ww - 1; {segment of |byte_mem|} | |
p: name_pointer; {current node of the search tree} | |
q: name_pointer; {another place to resume the search after one branch is done} | |
r: name_pointer; {extension found} | |
begin | |
q := 0; | |
p := rlink[0]; | |
count := 0; | |
r := 0; {begin search at root of tree} | |
while p <> 0 do | |
begin | |
begin | |
k := byte_start[p]; | |
w := p mod ww; | |
c := equal; | |
j := 1; | |
while (k < byte_start[p + ww]) and (j <= l) and (mod_text[j] = byte_mem[w, k]) do | |
begin | |
incr(k); | |
incr(j); | |
end; | |
if k = byte_start[p + ww] then | |
if j > l then | |
c := equal | |
else | |
c := extension | |
else | |
if j > l then | |
c := prefix | |
else | |
if mod_text[j] < byte_mem[w, k] then | |
c := less | |
else | |
c := greater; | |
end; | |
if c = less then | |
p := llink[p] | |
else | |
if c = greater then | |
p := rlink[p] | |
else | |
begin | |
r := p; | |
incr(count); | |
q := rlink[p]; | |
p := llink[p]; | |
end; | |
if p = 0 then | |
begin | |
p := q; | |
q := 0; | |
end; | |
end; | |
if count <> 1 then | |
if count = 0 then | |
err_print('! Name does not match') | |
else | |
err_print('! Ambiguous prefix'); | |
prefix_lookup := r; | |
end; | |
procedure store_two_bytes(x: sixteen_bits); {stores high byte, then low byte} | |
begin | |
if tok_ptr[z] + 2 > max_toks then | |
overflow('token'); | |
tok_mem[z, tok_ptr[z]] := x div 0400; {this could be done by a shift command} | |
tok_mem[z, tok_ptr[z] + 1] := x mod 0400; {this could be done by a logical and} | |
tok_ptr[z] := tok_ptr[z] + 2; | |
end; | |
procedure push_level(p: name_pointer); {suspends the current level} | |
begin | |
if stack_ptr = stack_size then | |
overflow('stack') | |
else | |
begin | |
stack[stack_ptr] := cur_state; {save |cur_end|, |cur_byte|, etc.} | |
incr(stack_ptr); | |
print_nl(stack_ptr); | |
cur_name := p; | |
cur_repl := equiv[p]; | |
zo := cur_repl mod zz; | |
cur_byte := tok_start[cur_repl]; | |
cur_end := tok_start[cur_repl + zz]; | |
cur_mod := 0; | |
end; | |
end; | |
procedure pop_level; {do this when |cur_byte| reaches |cur_end|} | |
label exit; | |
begin | |
if text_link[cur_repl] = 0 then {end of macro expansion} | |
begin | |
if ilk[cur_name] = parametric then | |
begin | |
decr(name_ptr); | |
decr(text_ptr); | |
z := text_ptr mod zz; | |
tok_ptr[z] := tok_start[text_ptr]; | |
end; | |
end | |
else | |
if text_link[cur_repl] < module_flag then {link to a continuation} | |
begin | |
cur_repl := text_link[cur_repl]; {we will stay on the same level} | |
zo := cur_repl mod 3; | |
cur_byte := tok_start[cur_repl]; | |
cur_end := tok_start[cur_repl + zz]; | |
return; | |
end; | |
decr(stack_ptr); {we will go down to the previous level} | |
if stack_ptr > 0 then | |
begin | |
cur_state := stack[stack_ptr]; | |
zo := cur_repl mod zz; | |
end; | |
exit: | |
end; | |
function get_output: sixteen_bits; {returns next token after macro expansion} | |
label | |
restart, done, found; | |
var | |
a: sixteen_bits; {value of current byte} | |
b: eight_bits; {byte being copied} | |
bal: sixteen_bits; {excess of \.( versus \.) while copying a parameter} | |
k: 0..max_bytes; {index into |byte_mem|} | |
w: 0..ww - 1; {segment of |byte_mem|} | |
begin | |
restart: | |
if stack_ptr = 0 then | |
begin | |
a := 0; | |
goto found; | |
end; | |
if cur_byte = cur_end then | |
begin | |
cur_val := -cur_mod; | |
pop_level; | |
if cur_val = 0 then | |
goto restart; | |
a := module_number; | |
goto found; | |
end; | |
a := tok_mem[zo, cur_byte]; | |
incr(cur_byte); | |
if a < 0200 then {one-byte token} | |
if a = param then | |
begin | |
push_level(name_ptr - 1); | |
goto restart; | |
end | |
else goto found; | |
a := (a - 0200) * 0400 + tok_mem[zo, cur_byte]; | |
cur_byte := cur_byte + 1; | |
if a < 024000 then {|@'24000=(@'250-@'200)*@'400|} | |
begin | |
case ilk[a] of | |
normal: | |
begin | |
cur_val := a; | |
a := identifier; | |
end; | |
numeric: | |
begin | |
cur_val := equiv[a] - 010000; | |
a := number; | |
end; | |
simple: | |
begin | |
push_level(a); | |
goto restart; | |
end; | |
parametric: | |
begin | |
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; | |
goto restart; | |
end; | |
bal := 1; | |
incr(cur_byte); | |
while true do | |
begin | |
b := tok_mem[zo, cur_byte]; | |
incr(cur_byte); | |
if b = 0 then | |
store_two_bytes(name_ptr + 077777) | |
else | |
begin | |
if b >= 0200 then | |
begin | |
app_repl(b); | |
b := tok_mem[zo, cur_byte]; | |
incr(cur_byte); {skip the opening `\.('} | |
end | |
else | |
case b of | |
"(": | |
bal := bal + 1; | |
")": | |
begin | |
bal := bal - 1; | |
if bal = 0 then goto 30; | |
end; | |
"'": | |
repeat | |
begin | |
if tok_ptr[z] = max_toks then | |
overflow('token'); | |
tok_mem[z, tok_ptr[z]] := b; | |
tok_ptr[z] := tok_ptr[z] + 1; | |
end; | |
b := tok_mem[zo, cur_byte]; | |
incr(cur_byte); | |
until b = "'"; {copy string, don't change |bal|} | |
others: | |
end; | |
app_repl(b) | |
end; | |
end; | |
done:; | |
equiv[name_ptr] := text_ptr; | |
ilk[name_ptr] := simple; | |
w := name_ptr mod ww; | |
k := byte_ptr[w]; | |
if name_ptr > max_names - ww then | |
overflow('name'); | |
byte_start[name_ptr + ww] := k; | |
name_ptr := name_ptr + 1; | |
if text_ptr > max_texts - zz then | |
overflow('text'); | |
text_link[text_ptr] := 0; | |
tok_start[text_ptr + zz] := tok_ptr[z]; | |
incr(text_ptr); | |
z := text_ptr mod zz; | |
push_level(a); | |
goto restart; | |
end; | |
others: | |
confusion('output') | |
end; | |
goto found; | |
end; | |
if a < 050000 then {|@'50000=(@'320-@'200)*@'400|} | |
begin | |
a := a - 024000; | |
if equiv[a] <> 0 then | |
push_level(a) | |
else | |
if a <> 0 then | |
begin | |
print_nl('! not present: <'); | |
print_id(a); | |
print('>'); | |
error; | |
end; | |
goto restart; | |
end; | |
cur_val := a - 050000; | |
a := module_number; | |
cur_mod := cur_val; | |
found: | |
get_output := a; | |
end; | |
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); | |
line := line + 1; | |
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; | |
end; | |
end; | |
procedure app_val(v: integer); {puts |v| into buffer, assumes |v>=0|} | |
var | |
k: 0..out_buf_size; {index into |out_buf|} | |
begin | |
k := out_buf_size; {first we put the digits at the very end of |out_buf|} | |
repeat | |
out_buf[k] := v mod 10; | |
v := v div 10; | |
decr(k); | |
until v = 0; | |
repeat | |
incr(k); | |
app(out_buf[k]+"0"); | |
until k = out_buf_size; {then we append them, most significant first} | |
end; | |
procedure send_out(t: eight_bits; v: sixteen_bits); {outputs |v| of type |t|} | |
label | |
restart; | |
var | |
k: 0..line_length; {index into |out_contrib|} | |
begin | |
restart: | |
case out_state of | |
num_or_id: | |
if t <> frac then | |
begin | |
break_ptr := out_ptr; | |
if t = ident then | |
app(" "); | |
end; | |
sign: | |
begin | |
app("," - out_app); | |
if out_ptr > line_length then flush_buffer; | |
break_ptr := out_ptr; | |
end; | |
sign_val, sign_val_sign: | |
begin | |
if (out_val < 0) or ((out_val = 0) and (last_sign<0)) then | |
app("-") | |
else | |
if out_sign > 0 then | |
app(out_sign); | |
app_val(abs(out_val)); | |
if out_ptr > line_length then | |
flush_buffer;; | |
out_state := out_state - 2; | |
goto restart; | |
end; | |
sign_val_val: | |
begin | |
if (t = frac) or (((t = ident) and (v = 3) and | |
(((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 | |
((t = misc) and ((v = "*") or (v = "/")))) then | |
begin | |
if (out_val < 0) or ((out_val = 0) and (last_sign < 0)) then | |
app("-") | |
else | |
if out_sign > 0 then | |
app(out_sign); | |
app_val(abs(out_val)); | |
check_break; | |
out_sign := "+"; | |
out_val := out_app; | |
end | |
else | |
out_val := out_val + out_app; | |
out_state := sign_val; | |
goto restart; | |
end; | |
misc: | |
if t <> frac then | |
break_ptr := out_ptr; | |
others:; {this is for |unbreakable| state} | |
end; | |
if t <> 0 then | |
for k := 1 to v do | |
app(out_contrib[k]) | |
else | |
app(v); | |
check_break; | |
if (t = misc) and ((v = ";") or (v = "}")) then | |
begin | |
semi_ptr := out_ptr; | |
break_ptr := out_ptr; | |
end; | |
if t >= ident then | |
out_state := num_or_id {|t=ident| or |frac|} | |
else | |
out_state := 0 {|t=str| or |misc|} | |
end; | |
procedure send_sign(v: integer); | |
begin | |
case out_state of | |
2, 4: | |
out_app := out_app * v; | |
3: | |
begin | |
out_app := v; | |
out_state := sign_val_sign; | |
end; | |
5: | |
begin | |
out_val := out_val + out_app; | |
out_app := v; | |
out_state := sign_val_sign; | |
end; | |
others: | |
begin | |
break_ptr := out_ptr; | |
out_app := v; | |
out_state := sign; | |
end | |
end; | |
last_sign := out_app; | |
end; | |
procedure send_val(v: integer); {output the (signed) value |v|} | |
label | |
bad_case, {go here if we can't keep |v| in the output state} | |
exit; | |
begin | |
case out_state of | |
num_or_id: | |
begin | |
if (out_ptr = break_ptr + 3)or((out_ptr = break_ptr + 4)and(out_buf[break_ptr] = 32)) then | |
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; | |
out_sign := " "; | |
out_state := sign_val; | |
out_val := v; | |
break_ptr := out_ptr; | |
last_sign := +1; | |
end; | |
misc: | |
begin | |
if (out_ptr = break_ptr + 1) and ((out_buf[break_ptr] = "*") or | |
(out_buf[break_ptr] = "/")) then | |
goto bad_case; | |
out_sign := 0; | |
out_state := sign_val; | |
out_val := v; | |
break_ptr := out_ptr; | |
last_sign := + 1; | |
end; | |
sign: | |
begin | |
out_sign := "+"; | |
out_state := sign_val; | |
out_val := out_app * v; | |
end; | |
sign_val: | |
begin | |
out_state := sign_val_val; | |
out_app := v; | |
err_print('! Two numbers occurred without a sign between them'); | |
end; | |
sign_val_sign: | |
begin | |
out_state := sign_val_val; | |
out_app := out_app * v; | |
end; | |
sign_val_val: | |
begin | |
out_val := out_val + out_app; | |
out_app := v; | |
err_print('! Two numbers occurred without a sign between them'); | |
end; | |
others: goto bad_case | |
end; | |
return; | |
bad_case: | |
if v >= 0 then | |
begin | |
if out_state = num_or_id then | |
begin | |
break_ptr := out_ptr; | |
app(" ") | |
end; | |
app_val(v); | |
check_break; | |
out_state := num_or_id; | |
end | |
else | |
begin | |
app("("); | |
app("-"); | |
app_val(-v); | |
app(")"); | |
check_break; | |
out_state := misc; | |
end | |
10: | |
end; | |
procedure send_the_output; | |
label | |
get_fraction, {go here to finish scanning a real constant} | |
reswitch, continue; | |
var | |
cur_char: eight_bits; | |
k: 0..line_length; | |
j: 0..max_bytes; | |
w: 0..1; | |
n: integer; | |
begin | |
while stack_ptr > 0 do | |
begin | |
cur_char := get_output; | |
reswitch: | |
case cur_char of | |
0:; {this case might arise if output ends unexpectedly} | |
"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 - 32; | |
send_out(ident, 1); | |
end; | |
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] - 040 | |
else | |
if out_contrib[k] = "_" then | |
decr(k); | |
end; | |
send_out(2, k); | |
end; | |
digits: | |
begin | |
n := 0; | |
repeat | |
cur_char := cur_char - "0"; | |
if n >= 01463146314 then | |
err_print('! Constant too big') | |
else | |
n := 10 * n + cur_char; | |
cur_char := get_output; | |
until (cur_char > "9")or(cur_char < "0"); | |
send_val(n); | |
k := 0; | |
if cur_char = "e" then | |
cur_char := "E"; | |
if cur_char = "E" then | |
goto get_fraction | |
else | |
goto reswitch; | |
end; | |
check_sum: | |
send_val(pool_check_sum); | |
octal: | |
begin | |
n := 0; | |
cur_char := "0"; | |
repeat | |
cur_char := cur_char - "0"; | |
if n >= 02000000000 then | |
err_print('! Constant too big') | |
else | |
n := 8 * n + cur_char; | |
cur_char := get_output; | |
until (cur_char > "7") or (cur_char < "0"); | |
send_val(n); | |
goto reswitch; | |
end; | |
hex: | |
begin | |
n := 0; | |
cur_char := "0"; | |
repeat | |
if cur_char >= "A" then | |
cur_char := cur_char + 10 - "A" | |
else | |
cur_char := cur_char - "0"; | |
if n >= 08000000 then | |
err_print('! Constant too big') | |
else | |
n := 16 * n + cur_char; | |
cur_char := get_output; | |
until (cur_char > "F") or (cur_char < "0") or | |
((cur_char > "9") and (cur_char < "A")); | |
send_val(n); | |
goto reswitch; | |
end; | |
number: | |
send_val(cur_val); | |
".": | |
begin | |
k := 1; | |
out_contrib[1] := "."; | |
cur_char := get_output; | |
if cur_char = "." then | |
begin | |
out_contrib[2] := "."; | |
send_out(str, 2); | |
end | |
else | |
if (cur_char >= "0") and (cur_char< = "9") then | |
goto get_fraction | |
else | |
begin | |
send_out(misc, "."); | |
goto 21; | |
end; | |
end; | |
"+", "-": | |
send_sign("," - cur_char); | |
and_sign: | |
begin | |
out_contrib[1]:="A"; | |
out_contrib[2]:="N"; | |
out_contrib[3]:="D"; | |
send_out(ident,3); | |
end; | |
not_sign: | |
begin | |
out_contrib[1]:="N"; | |
out_contrib[2]:="O"; | |
out_contrib[3]:="T"; | |
send_out(ident,3); | |
end; | |
set_element_sign: | |
begin | |
out_contrib[1]:="I"; | |
out_contrib[2]:="N"; | |
send_out(ident,2); | |
end; | |
or_sign: | |
begin | |
out_contrib[1]:="O"; | |
out_contrib[2]:="R"; | |
send_out(ident,2); | |
end; | |
left_arrow: | |
begin | |
out_contrib[1]:=":"; | |
out_contrib[2]:="="; | |
send_out(str,2); | |
end; | |
not_equal: | |
begin | |
out_contrib[1]:="<"; | |
out_contrib[2]:=">"; | |
send_out(str,2); | |
end; | |
less_or_equal: | |
begin | |
out_contrib[1]:="<"; | |
out_contrib[2]:="="; | |
send_out(str,2); | |
end; | |
greater_or_equal: | |
begin | |
out_contrib[1]:=">"; | |
out_contrib[2]:="="; | |
send_out(str,2); | |
end; | |
equivalence_sign: | |
begin | |
out_contrib[1]:="="; | |
out_contrib[2]:="="; | |
send_out(str,2); | |
end; | |
double_dot: | |
begin | |
out_contrib[1]:="."; | |
out_contrib[2]:="."; | |
send_out(str,2); | |
end; | |
"'": | |
begin | |
k := 1; | |
out_contrib[1] := "'"; | |
repeat | |
if k < line_length then | |
incr(k); | |
out_contrib[k] := get_output; | |
until (out_contrib[k] = "'") or (stack_ptr = 0); | |
if k = line_length then | |
err_print('! String too long'); | |
send_out(str, k); | |
cur_char := get_output; | |
if cur_char = "'" then | |
out_state := unbreakable; | |
goto reswitch; | |
end; | |
"!", """", "#", "$", "%", "&", "(", ")", "*", ",", "/", ":", ";", "<", "=", ">", "?", | |
"@@", "[", "\\", "]", "^", "_", "`", '{', "|": | |
send_out(0, cur_char); | |
begin_comment: | |
begin | |
if brace_level = 0 then | |
send_out(misc, "{") | |
else | |
send_out(misc, "["); | |
incr(brace_level); | |
end; | |
end_comment: | |
if brace_level>0 then | |
begin | |
decr(brace_level); | |
if brace_level = 0 then | |
send_out(misc,"}") | |
else | |
send_out(misc,"]"); | |
end | |
else err_print('! Extra @@}'); | |
module_number: | |
begin | |
k := 2; | |
if brace_level = 0 then | |
out_contrib[1] := "{" | |
else | |
out_contrib[1] := "["; | |
if cur_val < 0 then | |
begin | |
out_contrib[k] := ":"; | |
cur_val := -cur_val; | |
incr(k); | |
end; | |
n := 10; | |
while cur_val >= n do | |
n := 10 * n; | |
repeat | |
n := n div 10; | |
out_contrib[k] := "0" + (cur_val div n); | |
cur_val := cur_val mod n; | |
incr(k); | |
until n = 1; | |
if out_contrib[2] <> ":" then | |
begin | |
out_contrib[k] := ":"; | |
incr(k); | |
end; | |
if brace_level=0 then | |
out_contrib[k] := "}" | |
else | |
out_contrib[k] := "]"; | |
send_out(str, k); | |
end; | |
join: | |
begin | |
send_out(frac, 0); | |
out_state := 6; | |
end; | |
verbatim: | |
begin | |
k := 0; | |
repeat | |
if k < line_length then k := k + 1; | |
out_contrib[k] := get_output; | |
until (out_contrib[k] = 2) or (stack_ptr = 0); | |
if k = line_length then | |
err_print('! Verbatim string too long'); | |
send_out(str, k - 1); | |
end; | |
force_line: | |
begin | |
send_out(str, 0); {normalize the buffer} | |
while out_ptr > 0 do | |
begin | |
if out_ptr <= line_length then | |
break_ptr := out_ptr; | |
flush_buffer; | |
end; | |
out_state := misc; | |
end; | |
others: | |
err_print('! Can''t output ascii code ', cur_char:1); | |
end; | |
goto continue; | |
get_fraction: | |
repeat | |
if k < line_length then | |
incr(k); | |
out_contrib[k] := cur_char; | |
cur_char := get_output; | |
if (out_contrib[k] = "E") and ((cur_char = "+") or (cur_char = "-"))then | |
begin | |
if k < line_length then | |
incr(k); | |
out_contrib[k] := cur_char; | |
cur_char := get_output; | |
end | |
else | |
if cur_char = "e" then | |
cur_char := "E"; | |
until (cur_char <> "E") and ((cur_char < "0") or (cur_char > "9")); | |
if k = line_length then | |
err_print('! Fraction too long'); | |
send_out(frac, k); | |
goto reswitch; | |
continue: | |
end; | |
end; | |
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; | |
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} | |
while true do | |
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?'); | |
end; | |
continue: | |
end; | |
done:; | |
repeat | |
incr(line); | |
if not input_ln(change_file) then | |
begin | |
err_print('! Change file ended after @@x'); | |
return; | |
end; | |
until limit > 0;; | |
begin | |
change_limit := limit; | |
if limit > 0 then | |
for k := 0 to limit - 1 do | |
change_buffer[k] := buffer[k]; | |
end; | |
exit: | |
end; | |
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; | |
while true do | |
begin | |
changing := not changing; | |
temp_line := other_line; | |
other_line := line; | |
line := temp_line; | |
line := line + 1; | |
if not input_ln(change_file)then | |
begin | |
err_print('! Change file ended before @@y'); | |
change_limit := 0; | |
change_changing; {|false| again} | |
return; | |
end; | |
if limit > 1 then | |
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] = "z") then | |
begin | |
loc := 2; | |
err_print('! Where is the matching @y?'); | |
end | |
else | |
if buffer[1] = "y" then | |
begin | |
if n > 0 then | |
begin | |
loc := 2; | |
err_print('! Hmm... ', n:1, ' of the preceding lines failed to match'); | |
end; | |
return; | |
end; | |
end; | |
change_limit := limit; | |
if limit > 0 then | |
for k := 0 to limit - 1 do | |
change_buffer[k] := buffer[k]; | |
change_changing; {now it's |false|} | |
incr(line); | |
if not input_ln(web_file)then | |
begin | |
err_print('! Web file ended during a change'); | |
input_has_ended := true; | |
return; | |
end; | |
if lines_dont_match then | |
incr(n); | |
end; | |
exit: | |
end; | |
procedure get_line; {inputs the next line} | |
label | |
restart; | |
begin | |
restart: | |
if changing then | |
begin | |
incr(line); | |
if not input_ln(change_file) then | |
begin | |
err_print('! change file ended without @z'); | |
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?'); | |
end | |
else | |
if buffer[1] = "z" then | |
begin | |
prime_the_change_buffer; | |
change_changing; | |
end; | |
end; | |
end; | |
if not changing then | |
begin | |
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; | |
if changing then | |
goto restart; | |
end; | |
loc := 0; | |
buffer[limit] := " "; | |
end; | |
function control_code(c: ASCII_code): eight_bits; {convert |c| after \.{@@}} | |
begin | |
case c of | |
"@@": | |
control_code:="@@"; {`quoted' at sign} | |
"'": | |
control_code:=octal; {precedes octal constant} | |
"""": | |
control_code:=hex; {precedes hexadecimal constant} | |
"$": | |
control_code:=check_sum; {string pool check sum} | |
" ", tab_mark: | |
control_code:=new_module; {beginning of a new module} | |
"*": | |
begin | |
print('*',module_count+1:1); | |
update_terminal; {print a progress report} | |
control_code:=new_module; {beginning of a new module} | |
end; | |
"D","d": | |
control_code:=definition; {macro definition} | |
"F","f": | |
control_code:=format; {format definition} | |
"{": | |
control_code:=begin_comment; {begin-comment delimiter} | |
"}": | |
control_code:=end_comment; {end-comment delimiter} | |
"P","p": | |
control_code:=begin_Pascal; {\PASCAL\ text in unnamed module} | |
"T","t","^",".",":": | |
control_code:=control_text; {control text to be ignored} | |
"&": | |
control_code:=join; {concatenate two tokens} | |
"<": | |
control_code:=module_name; {beginning of a module name} | |
"=": | |
control_code:=verbatim; {beginning of \PASCAL\ verbatim mode} | |
"\\": | |
control_code:=force_line; {force a new line in \PASCAL\ output} | |
others: | |
control_code := ignore {ignore all other cases} | |
end; | |
end; | |
function skip_ahead: eight_bits; {skip to next control code} | |
label | |
done; | |
var | |
c: eight_bits; {control code found} | |
begin | |
while true do | |
begin | |
if loc > limit then | |
begin | |
get_line; | |
if input_has_ended then | |
begin | |
c := new_module; | |
goto done; | |
end; | |
end; | |
buffer[limit + 1] := "@"; | |
while buffer[loc] <> "@" do | |
incr(loc); | |
if loc <= limit then | |
begin | |
loc := loc + 2; | |
c := control_code(buffer[loc - 1]); | |
if (c <> ignore)or(buffer[loc - 1] = ">") then | |
goto done; | |
end; | |
end; | |
done: | |
skip_ahead := c; | |
end; | |
procedure skip_comment; (*skips to next unmatched `\.\}'*) | |
label exit; | |
var | |
bal: eight_bits; {excess of left braces} | |
c: ASCII_code; {current character} | |
begin | |
bal := 0; | |
while true do | |
begin | |
if loc > limit then | |
begin | |
get_line; | |
if input_has_ended then | |
begin | |
err_print('! Input ended in mid - comment'); | |
return; | |
end; | |
end; | |
c := buffer[loc]; | |
incr(loc); | |
if c = 64 then | |
begin | |
c := buffer[loc]; | |
if (c <> " ") and (c <> tab_mark) and (c <> "*") and (c <> "z") and (c <> "Z") then | |
incr(loc) | |
else | |
begin | |
err_print('! section ended in mid - comment'); | |
decr(loc); | |
return; | |
end | |
end | |
else | |
if (c = "\\") and (buffer[loc] <> "@") then | |
incr(loc) | |
else if c = "{" then | |
incr(bal) | |
else if c = 125 then | |
begin | |
if bal = 0 then | |
return; | |
decr(bal); | |
end; | |
end; | |
exit: | |
end; | |
function get_next: eight_bits; | |
label | |
restart, | |
done, | |
found; | |
var | |
c: eight_bits; {the current character} | |
d: eight_bits; {the next character} | |
j, k: 0..longest_name; {indices into |mod_text|} | |
begin | |
restart: | |
if loc > limit then | |
begin | |
get_line; | |
if input_has_ended then | |
begin | |
c := new_module; | |
goto found; | |
end; | |
end; | |
c := buffer[loc]; | |
loc := loc + 1; | |
if scanning_hex then | |
if ((c >= "0") and c <= "9")) or ((c >= "A") and (c <= "F")) then | |
goto found | |
else | |
scanning_hex := false; | |
case c of | |
"A", up_to("Z"), "a", up_to("z"): | |
begin | |
if ((c = "e") or (c = "E")) and (loc > 1) then | |
if (buffer[loc - 2]< = "9") and (buffer[loc - 2] >= "0") then | |
c := 0; | |
if c <> 0 then | |
begin | |
decr(loc); | |
id_first := loc; | |
repeat | |
incr(loc); | |
d := buffer[loc]; | |
until ((d < "0") or ((d > "9") and (d < "A")) or ((d > "Z") and (d < "a")) or | |
(d > "z")) and (d <> "_"); | |
if loc > id_first + 1 then | |
begin | |
c := identifier; | |
id_loc := loc; | |
end; | |
end | |
else c := "E"; {exponent of a real constant} | |
end; | |
"""": | |
begin | |
double_chars := 0; | |
id_first := loc - 1; | |
repeat | |
d := buffer[loc]; | |
incr(loc); | |
if (d = """") or (d = "@") then | |
if buffer[loc] = d then | |
begin | |
incr(loc); | |
d := 0; | |
incr(double_chars); | |
end | |
else | |
begin | |
if d = "@" then | |
err_print('! Double @ sign missing'); | |
end | |
else | |
if loc > limit then | |
begin | |
err_print('! String constant didn''t end'); | |
d := 34; | |
end; | |
until d = """"; | |
id_loc := loc - 1; | |
c := identifier; | |
end; | |
"@": | |
begin | |
c := control_code(buffer[loc]); | |
incr(loc); | |
if c = 0 then | |
goto restart | |
else if c = hex then | |
scanning_hex := true | |
else if c = module_name then | |
begin | |
k := 0; | |
while true do | |
begin | |
if loc > limit then | |
begin | |
get_line; | |
if input_has_ended then | |
begin | |
err_print('! Input ended in section name'); | |
goto done; | |
end; | |
end; | |
d := buffer[loc]; | |
if d = "@" then | |
begin | |
d := buffer[loc + 1]; | |
if d = ">" then | |
begin | |
loc := loc + 2; | |
goto done; | |
end; | |
if (d = " ") or (d = tab_mark) or (d = "*") then | |
begin | |
err_print('! section name didn''t end'); | |
goto done; | |
end; | |
incr(k); | |
mod_text[k] := "@"; | |
incr(loc); {now |d=buffer[loc]| again} | |
end; | |
loc := loc + 1; | |
if k<longest_name - 1 then k := k + 1; | |
if (d = 32)or(d = 9)then | |
begin | |
d := 32; | |
if mod_text[k - 1] = 32 then k := k - 1; | |
end; | |
mod_text[k] := d; | |
end; | |
done: if k >= longest_name - 2 then | |
begin | |
print_nl('! section name too long: '); | |
for j := 1 to 25 do | |
print(xchr[mod_text[j]]); | |
print('...'); | |
mark_harmless; | |
end; | |
if (mod_text[k] = " ") and (k > 0) then | |
decr(k); | |
if k > 3 then | |
begin | |
if (mod_text[k] = ".") and (mod_text[k - 1] = ".") | |
and (mod_text[k - 2] = ".") then | |
cur_mod := prefix_lookup(k - 3) | |
else | |
cur_mod := mod_lookup(k); | |
end | |
else cur_mod := mod_lookup(k); | |
end | |
else | |
if c = control_code then | |
begin | |
repeat | |
c := skip_ahead; | |
until c <> "@"; | |
if buffer[loc - 1] <> ">" then | |
err_print('! Improper @ within control text'); | |
goto restart; | |
end; | |
end; | |
".": | |
if buffer[loc]="." then | |
compress(double_dot) | |
else | |
if buffer[loc]=")" then | |
compress("]"); | |
":": | |
if buffer[loc]="=" then | |
compress(left_arrow); | |
"=": | |
if buffer[loc]="=" then | |
compress(equivalence_sign); | |
">": | |
if buffer[loc]="=" then | |
compress(greater_or_equal); | |
"<": | |
if buffer[loc]="=" then | |
compress(less_or_equal) | |
else | |
if buffer[loc]=">" then | |
compress(not_equal); | |
"(": | |
if buffer[loc]="*" then | |
compress(begin_comment) | |
else | |
if buffer[loc]="." then | |
compress("["); | |
"*": | |
if buffer[loc]=")" then | |
compress(end_comment); | |
" ", tab_mark: goto restart; | |
"{": | |
begin | |
skip_comment; | |
goto 20; | |
end; | |
"}": | |
begin | |
err_print('! extra }'); | |
goto restart; | |
end; | |
others: | |
if c >= 128 then {ignore nonstandard characters} | |
goto restart | |
else | |
end; | |
found: | |
get_next := c; | |
end; | |
procedure scan_numeric(p:name_pointer); | |
label | |
reswitch, | |
done; | |
var | |
accumulator: integer; {accumulates sums} | |
next_sign: - 1.. + 1; {sign to attach to next value} | |
q: name_pointer; {points to identifiers being evaluated} | |
val: integer; {constants being evaluated} | |
begin | |
accumulator := 0; | |
next_sign := +1; | |
while true do | |
begin | |
next_control := get_next; | |
reswitch: | |
case next_control of | |
digits: | |
begin | |
val := 0; | |
repeat | |
val := 10 * val + next_control - "0"; | |
next_control := get_next; | |
until (next_control > "9") or (next_control < "0"); | |
add_in(val); | |
goto reswitch; | |
end; | |
octal: | |
begin | |
val := 0; | |
next_control := "0"; | |
repeat | |
val := 8*val + next_control - "0"; | |
next_control := get_next; | |
until (next_control > "7")or(next_control<"0"); | |
add_in(val); | |
goto reswitch; | |
end; | |
hex: | |
begin | |
val := 0; | |
next_control := "0"; | |
repeat | |
if next_control >= "A" then next_control := next_control + "0" + 10 - "A"; | |
val := 16 * val + next_control - "0"; | |
next_control := get_next; | |
until (next_control > "F") or (next_control<"0") or ((next_control > "9") and | |
(next_control < "A")); | |
add_in(val); | |
goto reswitch; | |
end; | |
identifier: | |
begin | |
q := id_lookup(normal); | |
if ilk[q] <> numeric then | |
begin | |
next_control := "*"; | |
goto reswitch; {leads to error} | |
end; | |
add_in(equiv[q] - 0100000); | |
end; | |
"+":; | |
"-": | |
next_sign := -next_sign; | |
format, definition, module_name, begin_Pascal, new_module: | |
goto done; | |
";": | |
err_print('! Omit semicolon in numeric definition'); | |
others: | |
begin | |
err_print('! Improper numeric definition will be flushed'); | |
repeat | |
next_control := skip_ahead | |
until end_of_definition(next_control); | |
if next_control = module_name then | |
begin {we want to scan the module name too} | |
loc := loc - 2; | |
next_control := get_next; | |
end; | |
accumulator := 0; | |
goto done; | |
end | |
end; | |
end; | |
done:; | |
if abs(accumulator) >= 0100000 then | |
begin | |
err_print('! Value too big: ', accumulator:1); | |
accumulator := 0; | |
end; | |
equiv[p] := accumulator + 0100000; {name |p| now is defined to equal |accumulator|} | |
end; | |
procedure scan_repl(t: eight_bits); {creates a replacement text} | |
label | |
continue, | |
done, | |
found, | |
reswitch; | |
var | |
a: sixteen_bits; {the current token} | |
b: ASCII_code; {a character from the buffer} | |
bal: eight_bits; {left parentheses minus right parentheses} | |
begin | |
bal := 0; | |
while true do | |
begin | |
continue: | |
a := get_next; | |
case a of | |
"(": | |
bal := bal + 1; | |
")": | |
if bal = 0 then | |
err_print('! extra )'); | |
else | |
decr(bal); | |
"'": | |
begin | |
b := "'"; | |
while true do | |
begin | |
app_repl(b); | |
if b = "@" then | |
if buffer[loc] = "@" then | |
incr(loc) {store only one \.{@@}} | |
else | |
err_print('! You should double @ signs in strings'); | |
if loc = limit then | |
begin | |
err_print('! String didn''t end'); | |
buffer[loc] := "'"; | |
buffer[loc + 1] := 0; | |
end; | |
b := buffer[loc]; | |
incr(loc); | |
if b = "'" then | |
begin | |
if buffer[loc] <> "'" then | |
goto found | |
else | |
begin | |
incr(loc); | |
app_repl(b); | |
end; | |
end; | |
end; | |
found: {now |a| holds the final |"'"| that will be stored} | |
end; | |
"#": | |
if t = parametric then | |
a := param; | |
identifier: | |
begin | |
a := id_lookup(0); | |
app_repl((a div 0400) + 0200); | |
a := a mod 0400; | |
end; | |
module_name: | |
if t <> module_name then | |
goto done | |
else | |
begin | |
app_repl((cur_mod div 0400) + 0250) | |
a := cur_mod mod 0400; | |
end; | |
verbatim: | |
begin | |
app_repl(verbatim); | |
buffer[limit + 1] := "@"; | |
reswitch: if buffer[loc] = "@" then | |
begin | |
if loc < limit then | |
if buffer[loc + 1] = "@" then | |
begin | |
app_repl("@"); | |
loc := loc + 2; | |
goto reswitch; | |
end; | |
end | |
else | |
begin | |
app_repl(buffer[loc]); | |
incr(loc); | |
goto reswitch; | |
end; | |
if loc >= limit then | |
err_print('! Verbatim string didn''t end'); | |
else | |
if buffer[loc + 1] <> ">" then | |
err_print('! You should double @ signs in verbatim strings'); | |
loc := loc + 2; | |
end; {another |verbatim| byte will be stored, since |a=verbatim|} | |
definition, format, begin_Pascal: | |
if t <> module_name then | |
goto done | |
else | |
begin | |
err_print('! @', xchr[buffer[loc - 1]], ' is ignored in pascal text'); | |
goto continue; | |
end; | |
new_module: | |
goto done; | |
others: | |
end; | |
app_repl(a); | |
end; | |
end; | |
done: next_control := a; | |
if bal > 0 then | |
begin | |
if bal = 1 then | |
err_print('! missing )'); | |
else | |
err_print('! missing ', bal:1, ' )''s'); | |
while bal > 0 do | |
begin | |
app_repl(")") | |
decr(bal); | |
end; | |
end; | |
if text_ptr > max_texts - zz then | |
overflow('text'); | |
cur_repl_text := text_ptr; | |
tok_start[text_ptr + zz] := tok_ptr[z]; | |
incr(text_ptr); | |
if z = zz - 1 then | |
z := 0 | |
else | |
incr(z); | |
end; | |
procedure define_macro(t:eight_bits); | |
var | |
p: name_pointer; {the identifier being defined} | |
begin | |
p := id_lookup(t); | |
scan_repl(t); | |
equiv[p] := cur_repl_text; | |
text_link[cur_repl_text] := 0; | |
end; | |
procedure scan_module; | |
label | |
continue, | |
done, | |
exit; | |
var | |
p: name_pointer; {module name for the current module} | |
begin | |
incr(module_count); | |
next_control := 0; | |
while true do | |
begin | |
continue: | |
while next_control <= format do | |
begin | |
next_control := skip_ahead; | |
if next_control = module_name then | |
begin {we want to scan the module name too} | |
loc := loc - 2; | |
next_control := get_next; | |
end; | |
end; | |
if next_control <> definition then | |
goto done; | |
next_control := get_next; | |
if next_control <> identifier then | |
begin | |
err_print('! Definition flushed, must start with ', 'identifier of length > 1'); | |
goto continue; | |
end; | |
next_control := get_next; | |
if next_control = "=" then | |
begin | |
scan_numeric(id_lookup(numeric)); | |
goto continue; | |
end | |
else | |
if next_control = equivalence_sign then | |
begin | |
define_macro(simple); | |
goto continue; | |
end | |
else | |
if next_control = "(" then | |
begin | |
next_control := get_next; | |
if next_control = "#" then | |
begin | |
next_control := get_next; | |
if next_control = ")" then | |
begin | |
next_control := get_next; | |
if next_control = "=" then | |
begin | |
err_print('! Use == for macros'); | |
next_control := equivalence_sign; | |
end; | |
if next_control = equivalence_sign then | |
begin | |
define_macro(parametric); | |
goto continue; | |
end; | |
end; | |
end; | |
end; | |
err_print('! Definition flushed since it starts badly'); | |
end; | |
done:; | |
case next_control of | |
begin_Pascal: | |
p := 0; | |
module_name: | |
begin | |
p := cur_mod; | |
repeat | |
next_control := get_next; | |
until next_control <> "+"; (*allow optional `\.{+=}'*) | |
if (next_control <> "=")and(next_control <> equivalence_sign)then | |
begin | |
err_print('! Pascal text flushed, = sign is missing'); | |
repeat | |
next_control := skip_ahead; | |
until next_control = new_module; | |
return; | |
end; | |
end; | |
others: return | |
end; | |
store_two_bytes(0150000 + module_count); {|@'150000=@'320*@'400|} | |
scan_repl(135); {now |cur_repl_text| points to the replacement text} | |
if p = 0 then {unnamed module} | |
begin | |
text_link[last_unnamed] := cur_repl_text; | |
last_unnamed := cur_repl_text; | |
end | |
else | |
if equiv[p] = 0 then {first module of this name} | |
equiv[p] := cur_repl_text | |
else | |
begin | |
p := equiv[p]; | |
while text_link[p] < max_texts do {find end of list} | |
p := text_link[p]; | |
text_link[p] := cur_repl_text; | |
end; | |
text_link[cur_repl_text] := module_flag; {mark this replacement text as a nonmacro} | |
exit: | |
end; | |
begin | |
initialize; | |
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; | |
print_ln('This is TANGLE, version 4.5'); {print a ``banner line''} | |
phase_one := true; | |
module_count := 0; | |
repeat | |
next_control := skip_ahead; | |
until next_control = new_module; | |
while not input_has_ended do | |
scan_module; | |
if change_limit <> 0 then | |
begin | |
for ii := 0 to change_limit do | |
buffer[ii | |
] := change_buffer[ii]; | |
limit := change_limit; | |
changing := true; | |
line := other_line; | |
loc := change_limit; | |
begin | |
new_line; | |
print('! change file entry did not match'); | |
error; | |
end; | |
end; | |
phase_one := false; | |
if text_link[0] = 0 then | |
begin | |
print_nl('! No output was specified.'); | |
mark_harmless; | |
end | |
else | |
begin | |
print_nl('Writing the output file'); | |
update_terminal; | |
stack_ptr := 1; | |
brace_level := 0; | |
cur_name := 0; | |
cur_repl := text_link[0]; | |
zo := cur_repl mod zz; | |
cur_byte := tok_start[cur_repl]; | |
cur_end := tok_start[cur_repl + zz]; | |
cur_mod := 0; | |
out_state := misc; | |
out_ptr := 0; | |
break_ptr := 0; | |
semi_ptr := 0; | |
out_buf[0] := 0; | |
line := 1;; | |
send_the_output; | |
break_ptr := out_ptr; | |
semi_ptr := 0; | |
flush_buffer; | |
if brace_level <> 0 then | |
err_print('! Program ended at brace level ', brace_level:1); | |
print_nl('Done.'); | |
end; | |
end_of_TANGLE: | |
if string_ptr > 256 then | |
begin | |
print_nl(string_ptr - 256:1, ' strings written to string pool file.'); | |
write(pool, '*'); | |
for ii := 1 to 9 do | |
begin | |
out_buf[ii] := pool_check_sum mod 10; | |
pool_check_sum := pool_check_sum div 10; | |
end; | |
for ii := 9 downto 1 do | |
write(pool, xchr["0" + out_buf[ii]]); | |
write_ln(pool); | |
end; | |
case history of | |
0: | |
begin | |
new_line; | |
print('(No errors were found.)'); | |
end; | |
1: | |
begin | |
new_line; | |
print('(Did you see the warning message above?)'); | |
end; | |
2: | |
begin | |
new_line; | |
print('(Pardon me, but I think I spotted something wrong.)'); | |
end; | |
3: | |
begin | |
new_line; | |
print('(That was a fatal error, my friend.)'); | |
end; | |
end; | |
end. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment