Section 321: Maintaining the input stacks
The following subroutines change the input status in commonly needed ways.
First comes push_input, which stores the current state and creates a new level (having, initially, the same properties as the old).
// enter a new input level, save the old
#define push_input \
if (input_ptr > max_in_stack) { \
max_in_stack = input_ptr; \
if (input_ptr == STACK_SIZE) { \
overflow("input stack size", STACK_SIZE); \
} \
} \
input_stack[input_ptr] = cur_input; /* stack the record */ \
incr(input_ptr)
Section 322
And of course what goes up must come down.
// leave an input level, re-enter the old
#define pop_input \
decr(input_ptr); \
cur_input = input_stack[input_ptr]
Section 323
Here is a procedure that starts a new level of token-list input, given a token list p and its type t. If t = MACRO, the calling routine should set name and loc.
#define back_list(X) begin_token_list((X), BACKED_UP) // backs up a simple token list
#define ins_list(X) begin_token_list((X), INSERTED) // inserts a simple token list
void begin_token_list(pointer p, quarterword t) {
push_input;
state = TOKEN_LIST;
start = p;
token_type = t;
if (t >= MACRO) {
// the token list starts with a reference count
add_token_ref(p);
if (t == MACRO) {
param_start = param_ptr;
}
else {
loc = link(p);
if (tracing_macros > 1) {
begin_diagnostic();
print_nl("");
switch (t) {
case MARK_TEXT:
print_esc("mark");
break;
case WRITE_TEXT:
print_esc("write");
break;
default:
print_cmd_chr(ASSIGN_TOKS, t - OUTPUT_TEXT + OUTPUT_ROUTINE_LOC);
}
print("->");
token_show(p);
end_diagnostic(false);
}
}
}
else {
loc = p;
}
}
Section 324
When a token list has been fully scanned, the following computations should be done as we leave that level of input. The token_type tends to be equal to either BACKED_UP or INSERTED about 2/3 of the time.
// leave a token-list input level
void end_token_list() {
if (token_type >= BACKED_UP) {
// token list to be deleted
if (token_type <= INSERTED) {
flush_list(start);
}
else {
// update reference count
delete_token_ref(start);
if (token_type == MACRO) {
// parameters must be flushed
while (param_ptr > param_start) {
decr(param_ptr);
flush_list(param_stack[param_ptr]);
}
}
}
}
else if (token_type == U_TEMPLATE) {
if (align_state > 500000) {
align_state = 0;
}
else {
fatal_error("(interwoven alignment preambles are not allowed)");
}
}
pop_input;
check_interrupt;
}
Section 325
Sometimes has read too far and wants to “unscan” what it has seen. The back_input procedure takes care of this by putting the token just scanned back into the input stream, ready to be read again. This procedure can be used only if cur_tok represents the token to be replaced. Some applications of use this procedure a lot, so it has been slightly optimized for speed.
// undoes one token of input
void back_input() {
pointer p; // a token list of length one
while (state == TOKEN_LIST
&& loc == null
&& token_type != V_TEMPLATE)
{
// conserve stack space
end_token_list();
}
p = get_avail();
info(p) = cur_tok;
if (cur_tok < RIGHT_BRACE_LIMIT) {
if (cur_tok < LEFT_BRACE_LIMIT) {
decr(align_state);
}
else {
incr(align_state);
}
}
push_input;
state = TOKEN_LIST;
start = p;
token_type = BACKED_UP;
loc = p; // that was |back_list(p)|, without procedure overhead
}
Section 326
⟨ Insert token p into TeX’s input 326 ⟩≡
t = cur_tok;
cur_tok = p;
back_input();
cur_tok = t;
Section 327
The back_error routine is used when we want to replace an offending token just before issuing an error message. This routine, like back_input, requires that cur_tok has been set. We disable interrupts during the call of back_input so that the help message won’t be lost.
// back up one token and call |error|
void back_error() {
ok_to_interrupt = false;
back_input();
ok_to_interrupt = true;
error();
}
// back up one inserted token and call |error|
void ins_error() {
ok_to_interrupt = false;
back_input();
token_type = INSERTED;
ok_to_interrupt = true;
error();
}
Section 328
The begin_file_reading procedure starts a new level of input for lines of characters to be read from a file, or as an insertion from the terminal. It does not take care of opening the file, nor does it set loc or limit or line.
void begin_file_reading() {
if (in_open == MAX_IN_OPEN) {
overflow("text input levels", MAX_IN_OPEN);
}
if (first == BUF_SIZE) {
overflow("buffer size", BUF_SIZE);
}
incr(in_open);
push_input;
index = in_open;
line_stack[index] = line;
start = first;
state = MID_LINE;
name = 0; // |terminal_input| is now |true|
}
Section 329
Conversely, the variables must be downdated when such a level of input is finished:
void end_file_reading() {
first = start;
line = line_stack[index];
if (name > 17) {
a_close(cur_file); // forget it
}
pop_input;
decr(in_open);
}
Section 330
In order to keep the stack from overflowing during a long sequence of inserted ‘\show
’ commands, the following routine removes completed error-inserted lines from memory.
void clear_for_error_prompt() {
while (state != TOKEN_LIST
&& terminal_input
&& input_ptr > 0
&& loc > limit)
{
end_file_reading();
}
print_ln();
clear_terminal;
}
Section 331
To get ’s whole input mechanism going, we perform the following actions.
⟨ Initialize the input routines 331 ⟩≡
input_ptr = 0;
max_in_stack = 0;
in_open = 0;
open_parens = 0;
max_buf_stack = 0;
param_ptr = 0;
max_param_stack = 0;
memset(buffer, 0, BUF_SIZE + 1);
scanner_status = NORMAL;
warning_index = null;
first = 1;
state = NEW_LINE;
start = 1;
index = 0;
line = 0;
name = 0;
force_eof = false;
align_state = 1000000;
if (!init_terminal(argc, argv)) {
exit(0);
}
limit = last;
first = last + 1; // |init_terminal| has set |loc| and |last|