Section 268: Saving and restoring equivalents
The nested structure provided by ‘{...}
’ groups in means that eqtb entries valid in outer groups should be saved and restored later if they are overridden inside the braces.
When a new eqtb value is being assigned, the program therefore checks to see if the previous entry belongs to an outer level.
In such a case, the old value is placed on the save_stack just before the new value enters eqtb.
At the end of a grouping level, i.e., when the right brace is sensed, the save_stack is used to restore the outer values, and the inner ones are destroyed.
Entries on the save_stack are of type memory_word. The top item on this stack is save_stack[p], where p = save_ptr − 1; it contains three fields called save_type, save_level, and save_index, and it is interpreted in one of four ways:
-
If save_type(p) = RESTORE_OLD_VALUE, then save_index(p) is a location in eqtb whose current value should be destroyed at the end of the current group and replaced by save_stack[p − 1]. Furthermore if save_index(p) >= INT_BASE, then save_level(p) should replace the corresponding entry in xeq_level.
-
If save_type(p) = RESTORE_ZERO, then save_index(p) is a location in eqtb whose current value should be destroyed at the end of the current group, when it should be replaced by the value of eqtb[UNDEFINED_CONTROL_SEQUENCE].
-
If save_type(p) = INSERT_TOKEN, then save_index(p) is a token that should be inserted into ’s input when the current group ends.
-
If save_type(p) = LEVEL_BOUNDARY, then save_level(p) is a code explaining what kind of group we were previously in, and save_index(p) points to the level boundary word at the bottom of the entries for that group.
#define RESTORE_OLD_VALUE 0 // |save_type| when a value should be restored later
#define RESTORE_ZERO 1 // |save_type| when an undefined entry should be restored
#define INSERT_TOKEN 2 // |save_type| when a token is being saved for later use
#define LEVEL_BOUNDARY 3 // |save_type| corresponding to beginning of group
#define save_type(X) hh_b0(save_stack[(X)]) // classifies a |save_stack| entry
#define save_level(X) hh_b1(save_stack[(X)]) // saved level for regions 5 and 6, or group code
#define save_index(X) hh_rh(save_stack[(X)]) // |eqtb| location or token or |save_stack| location
Section 269
Here are the group codes that are used to discriminate between different kinds of groups. They allow to decide what special actions, if any, should be performed when a group ends.
Some groups are not supposed to be ended by right braces.
For example, the ‘$
’ that begins a math formula causes a MATH_SHIFT_GROUP to be started, and this should be terminated by a matching ‘$
’.
Similarly, a group that starts with \left
should end with \right
, and one that starts with \begingroup
should end with \endgroup
.
#define BOTTOM_LEVEL 0 // group code for the outside world
#define SIMPLE_GROUP 1 // group code for local structure only
#define HBOX_GROUP 2 // code for '\hbox{...}'
#define ADJUSTED_HBOX_GROUP 3 // code for '\hbox{...}' in vertical mode
#define VBOX_GROUP 4 // code for '\vbox{...}'
#define VTOP_GROUP 5 // code for '\vtop{...}'
#define ALIGN_GROUP 6 // code for '\halign{...}', '\valign{...}'
#define NO_ALIGN_GROUP 7 // code for '\noalign{...}'
#define OUTPUT_GROUP 8 // code for output routine
#define MATH_GROUP 9 // code for, e.g., `^{...}'
#define DISC_GROUP 10 // code for '\discretionary{...}{...}{...}'
#define INSERT_GROUP 11 // code for '\insert{...}', '\vadjust{...}'
#define VCENTER_GROUP 12 // code for '\vcenter{...}'
#define MATH_CHOICE_GROUP 13 // code for '\mathchoice{...}{...}{...}{...}'
#define SEMI_SIMPLE_GROUP 14 // code for '\begingroup...\endgroup'
#define MATH_SHIFT_GROUP 15 // code for '$...$'
#define MATH_LEFT_GROUP 16 // code for '\left...\right'
#define MAX_GROUP_CODE 16
Section 270
The global variable cur_group keeps track of what sort of group we are acurrently in. Another global variable, cur_boundary, points to the topmost LEVEL_BOUNDARY word. And cur_level is the current depth of nesting. The routines are designed to preserve the condition that no entry in the save_stack or in eqtb ever has a level greater than cur_level.
Section 271
⟨ Global variables 13 ⟩+≡
memory_word save_stack[SAVE_SIZE + 1];
int save_ptr; // first unused entry on |save_stack|
int max_save_stack; // maximum usage of save stack
quarterword cur_level; // current nesting level for groups
int cur_group; // current group type
int cur_boundary; // where the current level begins
Section 272
At this time it might be a good idea for the reader to review the introduction to eqtb that was given above just before the long lists of parameter names. Recall that the “outer level” of the program is LEVEL_ONE, since undefined control sequences are assumed to be “defined” at LEVEL_ZERO.
⟨ Set initial values of key variables 21 ⟩+≡
save_ptr = 0;
cur_level = LEVEL_ONE;
cur_group = BOTTOM_LEVEL;
cur_boundary = 0;
max_save_stack = 0;
Section 273
The following macro is used to test if there is room for up to six more entries on save_stack. By making a conservative test like this, we can get by with testing for overflow in only a few places.
#define check_full_save_stack \
do { \
if (save_ptr > max_save_stack) { \
max_save_stack = save_ptr; \
if (max_save_stack > SAVE_SIZE - 6) { \
overflow("save size", SAVE_SIZE); \
} \
} \
} while (0)
Section 274
Procedure new_save_level is called when a group begins. The argument is a group identification code like ‘HBOX_GROUP’. After calling this routine, it is safe to put five more entries on save_stack.
In some cases integer-valued items are placed onto the save_stack just below a LEVEL_BOUNDARY word, because this is a convenient place to keep information that is supposed to “pop up” just when the group has finished.
For example, when ‘\hbox to 100pt{...}
’ is being treated, the 100pt dimension is stored on save_stack just before new_save_level is called.
We use the notation saved(k) to stand for an integer item that appears in location save_ptr + k of the save stack.
#define saved(X) save_stack[save_ptr + (X)].integer
// << Start file |stack.c|, 1382 >>
// begin a new level of grouping
void new_save_level(int c) {
check_full_save_stack;
save_type(save_ptr) = LEVEL_BOUNDARY;
save_level(save_ptr) = cur_group;
save_index(save_ptr) = cur_boundary;
if (cur_level == MAX_QUARTERWORD) {
overflow("grouping levels", MAX_QUARTERWORD - MIN_QUARTERWORD);
} // quit if |(cur_level + 1)| is too big to be stored in |eqtb|
cur_boundary = save_ptr;
incr(cur_level);
incr(save_ptr);
cur_group = c;
}
Section 275
Just before an entry of eqtb is changed, the following procedure should be called to update the other data structures properly. It is important to keep in mind that reference counts in mem include references from within save_stack, so these counts must be handled carefully.
// gets ready to forget |w|
void eq_destroy(memory_word w) {
pointer q; // |equiv| field of |w|
switch(eq_type_field(w)) {
case CALL:
case LONG_CALL:
case OUTER_CALL:
case LONG_OUTER_CALL:
delete_token_ref(equiv_field(w));
break;
case GLUE_REF:
delete_glue_ref(equiv_field(w));
break;
case SHAPE_REF:
q = equiv_field(w); // we need to free a \parshape block
if (q != null) {
// such a block is |2n+1| words long, where |n=info(q)|
free_node(q, info(q) + info(q) + 1);
}
break;
case BOX_REF:
flush_node_list(equiv_field(w));
break;
default:
do_nothing;
}
}
Section 276
To save a value of eqtb[p] that was established at level l, we can use the following subroutine.
// saves |eqtb[p]|
void eq_save(pointer p, quarterword l) {
check_full_save_stack;
if (l == LEVEL_ZERO) {
save_type(save_ptr) = RESTORE_ZERO;
}
else {
save_stack[save_ptr] = eqtb[p];
incr(save_ptr);
save_type(save_ptr) = RESTORE_OLD_VALUE;
}
save_level(save_ptr) = l;
save_index(save_ptr) = p;
incr(save_ptr);
}
Section 277
The procedure eq_define defines an eqtb entry having specified eq_type and equiv fields, and saves the former value if appropriate. This procedure is used only for entries in the first four regions of eqtb, i.e., only for entries that have eq_type and equiv fields. After calling this routine, it is safe to put four more entries on save_stack, provided that there was room for four more entries before the call, since eq_save makes the necessary test.
// new data for |eqtb|
void eq_define(pointer p, quarterword t, halfword e) {
if (eq_level(p) == cur_level) {
eq_destroy(eqtb[p]);
}
else if (cur_level > LEVEL_ONE) {
eq_save(p, eq_level(p));
}
eq_level(p) = cur_level;
eq_type(p) = t;
equiv(p) = e;
}
Section 278
The counterpart of eq_define for the remaining (fullword) positions in eqtb is called eq_word_define. Since xeq_level[p] LEVEL_ONE for all p, a ‘RESTORE_ZERO’ will never be used in this case.
void eq_word_define(pointer p, int w) {
if (xeq_level[p] != cur_level) {
eq_save(p, xeq_level[p]);
xeq_level[p] = cur_level;
}
eqtb[p].integer = w;
}
Section 279
The eq_define and eq_word_define routines take care of local definitions. Global definitions are done in almost the same way, but there is no need to save old values, and the new value is associated with LEVEL_ONE.
// global |eq_define|
void geq_define(pointer p, quarterword t, halfword e) {
eq_destroy(eqtb[p]);
eq_level(p) = LEVEL_ONE;
eq_type(p) = t;
equiv(p) = e;
}
// global |eq_word_define|
void geq_word_define(pointer p, int w) {
eqtb[p].integer = w;
xeq_level[p] = LEVEL_ONE;
}
Section 280
Subroutine save_for_after puts a token on the stack for save-keeping.
void save_for_after(halfword t) {
if (cur_level > LEVEL_ONE) {
check_full_save_stack;
save_type(save_ptr) = INSERT_TOKEN;
save_level(save_ptr) = LEVEL_ZERO;
save_index(save_ptr) = t;
incr(save_ptr);
}
}
Section 281
The unsave routine goes the other way, taking items off of save_stack. This routine takes care of restoration when a level ends; everything belonging to the topmost group is cleared off of the save stack.
// << Declare the procedure called |restore_trace|, 284 >>
// pops the top level off the save stack
void unsave() {
pointer p; // position to be restored
quarterword l = 0; // saved level, if in fullword regions of |eqtb|
halfword t; // saved value of |cur_tok|
if (cur_level > LEVEL_ONE) {
decr(cur_level);
// << Clear off top level from |save_stack|, 282 >>
}
else {
// |unsave| is not used when |cur_group=bottom_level|
confusion("curlevel");
}
}
Section 282
⟨ Clear off top level from save_stack 282 ⟩≡
while(true) {
decr(save_ptr);
if (save_type(save_ptr) == LEVEL_BOUNDARY) {
break; // Goto done
}
p = save_index(save_ptr);
if (save_type(save_ptr) == INSERT_TOKEN) {
// << Insert token |p| into TeX's input, 326 >>
}
else {
if (save_type(save_ptr) == RESTORE_OLD_VALUE) {
l = save_level(save_ptr);
decr(save_ptr);
}
else {
save_stack[save_ptr] = eqtb[UNDEFINED_CONTROL_SEQUENCE];
}
// << Store |save_stack[save_ptr]| in |eqtb[p]|, unless |eqtb[p]| holds a global value, 283 >>
}
}
// done:
cur_group = save_level(save_ptr);
cur_boundary = save_index(save_ptr);
Section 283
A global definition, which sets the level to LEVEL_ONE, will not be undone by unsave. If at least one global definition of eqtb[p] has been carried out within the group that just ended, the last such definition will therefore survive.
⟨ Store save_stack[save_ptr] in eqtb[p], unless eqtb[p] holds a global value 283 ⟩≡
if (p < INT_BASE) {
if (eq_level(p) == LEVEL_ONE) {
eq_destroy(save_stack[save_ptr]); // destroy the saved value
#ifdef STAT
if (tracing_restores > 0) {
restore_trace(p, "retaining");
}
#endif
}
else {
eq_destroy(eqtb[p]); // destroy the current value
eqtb[p] = save_stack[save_ptr]; // restore the saved value
#ifdef STAT
if (tracing_restores > 0) {
restore_trace(p, "restoring");
}
#endif
}
}
else if (xeq_level[p] != LEVEL_ONE) {
eqtb[p] = save_stack[save_ptr];
xeq_level[p] = l;
#ifdef STAT
if (tracing_restores > 0) {
restore_trace(p, "restoring");
}
#endif
}
#ifdef STAT
else {
if (tracing_restores > 0) {
restore_trace(p, "retaining");
}
}
#endif
Section 284
⟨ Declare the procedure called restore_trace 284 ⟩≡
#ifdef STAT
// |eqtb[p]| has just been restored or retained
void restore_trace(pointer p, char *s) {
begin_diagnostic();
print_char('{');
print(s);
print_char(' ');
show_eqtb(p);
print_char('}');
end_diagnostic(false);
}
#endif
Section 285
When looking for possible pointers to a memory location, it is helpful to look for references from eqtb that might be waiting on the save stack. Of course, we might find spurious pointers too; but this routine is merely an aid when debugging, and at such times we are grateful for any scraps of information, even if they prove to be irrelevant.
⟨ Search save_stack for equivalents that point to p 285 ⟩≡
if (save_ptr > 0) {
for(q = 0; q < save_ptr; q++) {
if (equiv_field(save_stack[q]) == p) {
print_nl("SAVE(");
print_int(q);
print_char(')');
}
}
}
Section 286
Most of the parameters kept in eqtb can be changed freely, but there’s an exception: The magnification should not be used with two different values during any job, since a single magnification is applied to entire run. The global variable mag_set is set to the current magnification whenever it becomes necessary to “freeze” it at a particular value.
⟨ Global variables 13 ⟩+≡
int mag_set; // if nonzero, this magnification should be used henceforth
Section 287
⟨ Set initial values of key variables 21 ⟩+≡
mag_set = 0;
Section 288
The prepare_mag subroutine is called whenever wants to use mag for magnification.
void prepare_mag() {
if (mag_set > 0 && mag != mag_set) {
print_err("Incompatible magnification (");
print_int(mag);
print(");");
print_nl(" the previous value will be retained");
help2("I can handle only one magnification ratio per job. So I've")
("reverted to the magnification you used earlier on this run.");
int_error(mag_set);
geq_word_define(INT_BASE + MAG_CODE, mag_set); // |mag = mag_set|
}
if (mag <= 0 || mag > 32768) {
print_err("Illegal magnification has been changed to 1000");
help1("The magnification ratio must be between 1 and 32768.");
int_error(mag);
geq_word_define(INT_BASE + MAG_CODE, 1000);
}
mag_set = mag;
}