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).

datastructures.h
// 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.

datastructures.h
// 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.

datastructures.h
#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
stack.c
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.

stack.c
// 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.

stack.c
// 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.

stack.c
// 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.

stack.c
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:

stack.c
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.

error.c
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|