Skip to content

Instantly share code, notes, and snippets.

@texdraft
Created August 13, 2019 19:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save texdraft/c72fd78491439abd7d9d323fec3ec043 to your computer and use it in GitHub Desktop.
Save texdraft/c72fd78491439abd7d9d323fec3ec043 to your computer and use it in GitHub Desktop.
diff oldtex.web tex.web
This file has been truncated, but you can view the full file.
2,4c2,3
< \def\glob{13} % this should be the section number of "<Globals...>"
< \def\gglob{20, 26} % this should be the next two sections of "<Globals...>"
< This is \TeX, a document compiler intended to produce high-quality typesetting.
---
> This is \TeX, a document compiler intended to produce typesetting of high
> quality.
9c8
< will be obtainable on a great variety of different computers.
---
> will be obtainable on a great variety of computers.
34c33
< the \TeX\ user's manual.
---
> {\sl The \TeX book}.
35a35
> @:TeXbook}{\sl The \TeX book@>
38c38
< of@@1977, when Michael@@F. Plass and Frank@@M. Liang designed and coded
---
> of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded
43c43
< based on some specifications that the author had made in April of that year.
---
> based on some specifications that the author had made in May of that year.
54c54,55
< somewhat like the present ``web'' were developed by Luis Trabb@@Pardo and
---
> somewhat like the present ``web'' were developed by Luis Trabb~Pardo and
> @^Trabb Pardo, Luis Isidoro@>
56c57
< created by Ignacio@@A. Zabala in 1979 and 1980. The \TeX82 program, which
---
> created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which
67c68,76
< has been substantially improved.
---
> has been substantially improved. After the appearance of ``Version 0'' in
> September 1982, this program benefited greatly from the comments of
> many other people, notably David~R. Fuchs and Howard~W. Trickey.
> A final revision in September 1989 extended the input character set to
> eight-bit codes and introduced the ability to hyphenate words from
> different languages, based on some ideas of Michael~J. Ferguson.
> @^Fuchs, David Raymond@>
> @^Trickey, Howard Wellington@>
> @^Ferguson, Michael John@>
69c78
< No doubt there still is plenty of room for enhancements, but the author
---
> No doubt there still is plenty of room for improvement, but the author
71c80
< and reliabil\-ity are to be its main virtues.
---
> and reliability are to be its main virtues.
79a89
> @^system dependencies@>
81c91,99
< @d banner=='This is TeX, Version -0.25' {printed when \TeX\ starts}
---
> If this program is changed, the resulting system should not be called
> `\TeX'; the official name `\TeX' by itself is reserved
> for software systems that are fully compatible with each other.
> A special test suite called the ``\.{TRIP} test'' is available for
> helping to determine whether a particular implementation deserves to be
> known as `\TeX' [cf.~Stanford Computer Science report CS1027,
> November 1984].
>
> @d banner=='This is TeX, Version 3.14159265' {printed when \TeX\ starts}
84d101
< \def\ph{{\mc PASCAL-H}}
87c104
< available to the author in 1982. The methods used here to work with
---
> available to the author in 1982. Constructions that apply to
89,90c106,107
< reader to see how to make an appropriate interface for other systems
< if necessary. (\ph\ is Charles Hedrick's mod\-ifi\-ca\-tion of a compiler
---
> reader see how to make an appropriate interface for other systems
> if necessary. (\ph\ is Charles Hedrick's modification of a compiler
98,99c115,120
< \PASCAL\ itself, so that most of the code can be mechanically translated
< into other high-level languages.)
---
> \PASCAL\ itself, so that most of the code can be translated mechanically
> into other high-level languages. For example, the `\&{with}' and `\\{new}'
> features are not used, nor are pointer types, set types, or enumerated
> scalar types; there are no `\&{var}' parameters, except in the case of files;
> there are no tag fields on variant records; there are no assignments
> |real:=integer|; no procedures are declared local to other procedures.)
110a132,136
> Incidentally, \PASCAL's standard |round| function can be problematical,
> because it disagrees with the IEEE floating-point standard.
> Many implementors have
> therefore chosen to substitute their own home-grown rounding procedure.
>
114,115c140,141
< For example, the portion of the program called `\X\glob:Globals in the outer
< block\X' here will be replaced by a sequence of variable declarations
---
> For example, the portion of the program called `\X\glob:Global
> variables\X' below will be replaced by a sequence of variable declarations
120c146
< sections \gglob, $\ldots$,'' also make it possible to look at the set of
---
> sections \gglob, \dots,'' also make it possible to look at the set of
126a153
> @:PASCAL H}{\ph@>
138c165
< var@?@<Globals in the outer block@>@/
---
> var @<Global variables@>@/
141,143c168,170
< var@?@<Local variables for initialization@>@/
< begin @<Initialize whatever \TeX\ might access@>@;
< end;@#
---
> var @<Local variables for initialization@>@/
> begin @<Initialize whatever \TeX\ might access@>@;
> end;@#
150c177
< comment `@!|start_here|'. If you want to skip down to the
---
> comment `|start_here|'. If you want to skip down to the
167c194
< {key control points}
---
> {key control points}
173,177c200,207
< delimited by the codewords `$|debug|\ldotsm|gubed|$', with apologies
< to people who wish to preserve the purity of English. Similarly, there
< is some conditional code delimited by `$|stat|\ldotsm|tats|$'
< that is intended only for use when statistics
< are to be kept about \TeX's memory usage.
---
> delimited by the codewords `$|debug|\ldots|gubed|$', with apologies
> to people who wish to preserve the purity of English.
>
> Similarly, there is some conditional code delimited by
> `$|stat|\ldots|tats|$' that is intended for use when statistics are to be
> kept about \TeX's memory usage. The |stat| $\ldots$ |tats| code also
> implements diagnostic information for \.{\\tracingparagraphs} and
> \.{\\tracingpages}.
180,181c210,211
< @d debug==@{ {change this to `$\\{debug}\eqv\null$' when debugging}
< @d gubed==@} {change this to `$\\{gubed}\eqv\null$' when debugging}
---
> @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
> @d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
185,188c215,218
< @d stat==@{ {change this to `$\\{stat}\eqv\null$' when gathering
< usage statistics}
< @d tats==@} {change this to `$\\{tats}\eqv\null$' when gathering
< usage statistics}
---
> @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}
193c223
< version called \.{INITEX}, which does the extra calculations need to
---
> version called \.{INITEX}, which does the extra calculations needed to
195c225
< initialize \TeX's internal tables; and (2)@@there is a shorter and faster
---
> initialize \TeX's internal tables; and (2)~there is a shorter and faster
198c228
< the codewords `$|init|\ldotsm|tini|$'.
---
> the codewords `$|init|\ldots|tini|$'.
200,201c230,231
< @d init== {change this to `$\\{init}\eqv\.{@@\{}$' in the production version}
< @d tini== {change this to `$\\{tini}\eqv\.{@@\}}$' in the production version}
---
> @d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
> @d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
207c237
< init @<Initialize table entries (done by \.{INITEX} only)@>@;@+tini
---
> @!init @<Initialize table entries (done by \.{INITEX} only)@>@;@+tini
210d239
< @^system dependencies@>
217c246,248
< @^Overflow in arithmetic@>
---
> @:PASCAL H}{\ph@>
> @^system dependencies@>
> @^overflow in arithmetic@>
221c252
< debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
---
> @!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
223c254
< @ This \TeX\ implementation conforms to the rules of the {\sl PASCAL User
---
> @ This \TeX\ implementation conforms to the rules of the {\sl Pascal User
234c265
< $$\vbox{\halign{\!#\hfil\cr
---
> $$\vbox{\halign{\ignorespaces#\hfil\cr
238c269
< |othercases| $\langle\,$code for |x≠1| and |x≠3|$\,\rangle$\cr
---
> |othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr
243,248c274,279
< syntaxes like `\!|else|\unskip' or `\\{otherwise}' or `\\{otherwise}:',
< etc. The definitions of |othercases| and |endcases| should be changed to
< agree with local conventions. Note that no semicolon appears before
< |endcases| in this program, so the definition of |endcases| should include
< a semicolon if the compiler wants one. (Of course, if no default mechanism
< is available, the |case| statements of \TeX\ will have to be laboriously
---
> syntaxes like `\&{else}' or `\&{otherwise}' or `\\{otherwise}:', etc. The
> definitions of |othercases| and |endcases| should be changed to agree with
> local conventions. Note that no semicolon appears before |endcases| in
> this program, so the definition of |endcases| should include a semicolon
> if the compiler wants one. (Of course, if no default mechanism is
> available, the |case| statements of \TeX\ will have to be laboriously
250c281,282
< \PASCAL s have in fact done this, successfully but not happily!)
---
> \PASCAL s have, in fact, done this, successfully but not happily!)
> @:PASCAL H}{\ph@>
264,265c296,301
< @!mem_max=30000; {greatest index in \TeX's internal |mem| array,
< must be strictly less than |max_halfword|}
---
> @!mem_max=30000; {greatest index in \TeX's internal |mem| array;
> must be strictly less than |max_halfword|;
> must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|}
> @!mem_min=0; {smallest index in \TeX's internal |mem| array;
> must be |min_halfword| or more;
> must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|}
267,272c303,309
< current lines of open files; must not exceed |max_halfword|}
< @!error_line=64; {width of context lines on terminal error messages}
< @!half_error_line=32; {width of first lines of contexts in terminal
< error messages, should be between 30 and |error_line-15|}
< @!max_print_line=72; {width of longest text lines output, should be at least 60}
< @!stack_size=80; {maximum number of simultaneous input sources}
---
> current lines of open files and in control sequences between
> \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
> @!error_line=72; {width of context lines on terminal error messages}
> @!half_error_line=42; {width of first lines of contexts in terminal
> error messages; should be between 30 and |error_line-15|}
> @!max_print_line=79; {width of longest text lines output; should be at least 60}
> @!stack_size=200; {maximum number of simultaneous input sources}
274,278c311,315
< can be going on simultaneously}
< @!font_max=75; {maximum internal font number, must not exceed |max_quarterword|}
< @!bad_font_code=300; {user font codes must be less than this}
< @!font_mem_size=15000; {number of words of |font_info| for all fonts}
< @!param_size=30; {maximum number of simultaneous macro parameters}
---
> can be going on simultaneously}
> @!font_max=75; {maximum internal font number; must not exceed |max_quarterword|
> and must be at most |font_base+256|}
> @!font_mem_size=20000; {number of words of |font_info| for all fonts}
> @!param_size=60; {maximum number of simultaneous macro parameters}
282,292c319,330
< available for the user's control sequences and font names,
< after \TeX's own error messages are stored}
< @!pool_size=30000; {maximum number of characters in strings, including all
< error messages and help texts, and the names of all fonts and
< control sequences; must be at least 22000 more than |string_vacancies|}
< @!align_size=4; {maximum number of simultaneous alignments}
< @!save_size=300; {space for saving values outside of current group, must be
< at most |max_halfword|}
< @!trie_size=7000; {space for hyphenation patterns, should be larger for
< \.{INITEX} than it is in production versions of \TeX}
< @!dvi_buf_size=800; {size of the output buffer, must be a multiple of 8}
---
> available for the user's control sequences and font names,
> after \TeX's own error messages are stored}
> @!pool_size=32000; {maximum number of characters in strings, including all
> error messages and help texts, and the names of all fonts and
> control sequences; must exceed |string_vacancies| by the total
> length of \TeX's own strings, which is currently about 23000}
> @!save_size=600; {space for saving values outside of current group; must be
> at most |max_halfword|}
> @!trie_size=8000; {space for hyphenation patterns; should be larger for
> \.{INITEX} than it is in production versions of \TeX}
> @!trie_op_size=500; {space for ``opcodes'' in the hyphenation patterns}
> @!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
294,296c332,334
< @!pool_name='<TeX.sources>TEX.POOL ';
< {string of length |file_name_size|, tells where string pool appears}
< @^system dependencies@>
---
> @!pool_name='TeXformats:TEX.POOL ';
> {string of length |file_name_size|; tells where the string pool appears}
> @.TeXformats@>
309,314c347,353
< @d mem_base=0 {smallest index in the |mem| array, must not be less
< than |min_halfword|}
< @d hi_mem_base=12000 {smallest index in the single-word area of |mem|,
< must be substantially larger than |mem_base| and smaller than |mem_max|}
< @d font_base=0 {smallest internal font number, must not be less
< than |min_quarterword|}
---
> @d mem_bot=0 {smallest index in the |mem| array dumped by \.{INITEX};
> must not be less than |mem_min|}
> @d mem_top==30000 {largest index in the |mem| array dumped by \.{INITEX};
> must be substantially larger than |mem_bot|
> and not greater than |mem_max|}
> @d font_base=0 {smallest internal font number; must not be less
> than |min_quarterword|}
316,317c355,356
< about |(mem_max-hi_mem_base)/6|, but 2100 is already quite generous}
< @d hash_prime=1777 {a prime number equal to about 85\%\ of |hash_size|}
---
> about |(mem_max-mem_min)/10|}
> @d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
330,331c369,370
< @ Later on we will say `\!|if mem_max≥max_halfword then bad←10|', or
< something similar. (We can't do that until |max_halfword| has been defined.)
---
> @ Later on we will say `\ignorespaces|if mem_max>=max_halfword then bad:=14|',
> or something similar. (We can't do that until |max_halfword| has been defined.)
334,339c373,380
< bad←0;
< if (half_error_line<30)∨(half_error_line>error_line-15) then bad←1;
< if max_print_line<60 then bad←2;
< if dvi_buf_size mod 8≠0 then bad←3;
< if (hi_mem_base<mem_base+100)∨(hi_mem_base+100>mem_max) then bad←4;
< if hash_prime>hash_size then bad←5;
---
> bad:=0;
> if (half_error_line<30)or(half_error_line>error_line-15) then bad:=1;
> if max_print_line<60 then bad:=2;
> if dvi_buf_size mod 8<>0 then bad:=3;
> if mem_bot+1100>mem_top then bad:=4;
> if hash_prime>hash_size then bad:=5;
> if max_in_open>=128 then bad:=6;
> if mem_top<256+11 then bad:=7; {we will want |null_list>255|}
342,354c383,394
< occasional |goto| statements will be meaningful. We insert
< the label `|exit|:' just before the `\!|end|\unskip' of a procedure in
< which we have used the `|return|' statement defined below;
< the label `|restart|' is occasionally used at the very beginning of a
< procedure; and the label `|reswitch|' is occasionally used just prior to
< a |case| statement in which some cases change the conditions and we wish to
< branch to the newly applicable case.
< Loops that are set up with the |loop| construction defined below are
< commonly exited by going to `|done|' or to `|found|' or to `|not_found|',
< and they are sometimes repeated by going to `|continue|'.
< If two or more parts of a subroutine start differently but end up the same,
< the shared code may be gathered together at `|common_ending|'.
<
---
> occasional |goto| statements will be meaningful. We insert the label
> `|exit|' just before the `\ignorespaces|end|\unskip' of a procedure in
> which we have used the `|return|' statement defined below; the label
> `|restart|' is occasionally used at the very beginning of a procedure; and
> the label `|reswitch|' is occasionally used just prior to a |case|
> statement in which some cases change the conditions and we wish to branch
> to the newly applicable case. Loops that are set up with the |loop|
> construction defined below are commonly exited by going to `|done|' or to
> `|found|' or to `|not_found|', and they are sometimes repeated by going to
> `|continue|'. If two or more parts of a subroutine start differently but
> end up the same, the shared code may be gathered together at
> `|common_ending|'.
378,379c418,420
< @d incr(#) == #←#+1 {increase a variable by unity}
< @d decr(#) == #←#-1 {decrease a variable by unity}
---
> @d incr(#) == #:=#+1 {increase a variable by unity}
> @d decr(#) == #:=#-1 {decrease a variable by unity}
> @d negate(#) == #:=-# {change the sign of a variable}
381c422,423
< @f loop == xclause {\.{WEB}'s |xclause| acts like `\!|while true do|\unskip'}
---
> @f loop == xclause
> {\.{WEB}'s |xclause| acts like `\ignorespaces|while true do|\unskip'}
384a427,428
> @d empty=0 {symbolic name for a null constant}
>
386,388c430,432
< In order to make \TeX\ readily portable between a wide variety of
< computers, all of its input text is converted to an internal seven-bit
< code that is essentially standard ascii, the ``American Standard Code for
---
> In order to make \TeX\ readily portable to a wide variety of
> computers, all of its input text is converted to an internal eight-bit
> code that includes standard ASCII, the ``American Standard Code for
390c434
< character is read in. Conversely, characters are converted from ascii to
---
> character is read in. Conversely, characters are converted from ASCII to
392c436
< text file.
---
> text file.
396c440
< character `\.A' has ascii code $65=@'101$, and when \TeX\ typesets
---
> character `\.A' has ASCII code $65=@'101$, and when \TeX\ typesets
400c444
< \TeX's device-independent files is responsible for converting from ascii to
---
> \TeX's device-independent files is responsible for converting from ASCII to
402c446
< @^ascii code@>
---
> @^ASCII code@>
404c448
< \TeX's internal code is relevant also with respect to constants
---
> \TeX's internal code also defines the value of constants
406c450
< \.{\\chcode}, \.{\\mathcode}, \.{\\uccode}, \.{\\lccode}, and \.{\\delcode}
---
> \.{\\catcode}, \.{\\mathcode}, \.{\\uccode}, \.{\\lccode}, and \.{\\delcode}
409,410c453,454
< @ Characters of text that have been converted to \TeX's internal form
< are said to be of type |ascii_code|, which is a subrange of the integers.
---
> @ Characters of text that have been converted to \TeX's internal form
> are said to be of type |ASCII_code|, which is a subrange of the integers.
413c457
< @!ascii_code=0..127; {seven-bit numbers}
---
> @!ASCII_code=0..255; {eight-bit numbers}
416,418c460,462
< character sets were common, so it did not make provision for lower case
< letters. Nowadays, of course, we need to deal with both upper and lower case
< alphabets in a convenient way, especially in a program for typesetting;
---
> character sets were common, so it did not make provision for lowercase
> letters. Nowadays, of course, we need to deal with both capital and small
> letters in a convenient way, especially in a program for typesetting;
423c467
< with ascii codes @'40 through @'176; all of these characters are now
---
> with ASCII codes @'40 through @'176; all of these characters are now
435c479
< from |ascii_code| when they are input and output. We shall also assume
---
> from |ASCII_code| when they are input and output. We shall also assume
437c481
< |chr(last_text_char)|, in\-clu\-sive. The following definitions should be
---
> |chr(last_text_char)|, inclusive. The following definitions should be
443c487
< @d last_text_char=127 {ordinal number of the largest element of |text_char|}
---
> @d last_text_char=255 {ordinal number of the largest element of |text_char|}
446c490
< i:0..last_text_char;
---
> @!i:integer;
448c492
< @ The \TeX\ processor converts between ascii code and
---
> @ The \TeX\ processor converts between ASCII code and
452,462c496,509
< @<Globals...@>=
< @!xord: array [text_char] of ascii_code;
< {specifies conversion of input characters}
< @!xchr: array [ascii_code] of text_char;
< {specifies conversion of output characters}
<
< @ Since we are assuming that our \PASCAL\ system is able to read and write the
< visible characters of standard ascii (although not necessarily using the
< ascii codes to represent them), the following assignment statements initialize
< most of the |xchr| array properly, without needing any system-dependent
< changes.
---
> @<Glob...@>=
> @!xord: array [text_char] of ASCII_code;
> {specifies conversion of input characters}
> @!xchr: array [ASCII_code] of text_char;
> {specifies conversion of output characters}
>
> @ Since we are assuming that our \PASCAL\ system is able to read and
> write the visible characters of standard ASCII (although not
> necessarily using the ASCII codes to represent them), the following
> assignment statements initialize the standard part of the |xchr| array
> properly, without needing any system-dependent changes. On the other
> hand, it is possible to implement \TeX\ with less complete character
> sets, and in such cases it will be necessary to change something here.
> @^system dependencies@>
465,561c512,606
< xchr[@'40]←' ';
< xchr[@'41]←'!';
< xchr[@'42]←'"';
< xchr[@'43]←'#';
< xchr[@'44]←'$';
< xchr[@'45]←'%';
< xchr[@'46]←'&';
< xchr[@'47]←'''';@/
< xchr[@'50]←'(';
< xchr[@'51]←')';
< xchr[@'52]←'*';
< xchr[@'53]←'+';
< xchr[@'54]←',';
< xchr[@'55]←'-';
< xchr[@'56]←'.';
< xchr[@'57]←'/';@/
< xchr[@'60]←'0';
< xchr[@'61]←'1';
< xchr[@'62]←'2';
< xchr[@'63]←'3';
< xchr[@'64]←'4';
< xchr[@'65]←'5';
< xchr[@'66]←'6';
< xchr[@'67]←'7';@/
< xchr[@'70]←'8';
< xchr[@'71]←'9';
< xchr[@'72]←':';
< xchr[@'73]←';';
< xchr[@'74]←'<';
< xchr[@'75]←'=';
< xchr[@'76]←'>';
< xchr[@'77]←'?';@/
< xchr[@'100]←'@@';
< xchr[@'101]←'A';
< xchr[@'102]←'B';
< xchr[@'103]←'C';
< xchr[@'104]←'D';
< xchr[@'105]←'E';
< xchr[@'106]←'F';
< xchr[@'107]←'G';@/
< xchr[@'110]←'H';
< xchr[@'111]←'I';
< xchr[@'112]←'J';
< xchr[@'113]←'K';
< xchr[@'114]←'L';
< xchr[@'115]←'M';
< xchr[@'116]←'N';
< xchr[@'117]←'O';@/
< xchr[@'120]←'P';
< xchr[@'121]←'Q';
< xchr[@'122]←'R';
< xchr[@'123]←'S';
< xchr[@'124]←'T';
< xchr[@'125]←'U';
< xchr[@'126]←'V';
< xchr[@'127]←'W';@/
< xchr[@'130]←'X';
< xchr[@'131]←'Y';
< xchr[@'132]←'Z';
< xchr[@'133]←'[';
< xchr[@'134]←'\';
< xchr[@'135]←']';
< xchr[@'136]←'^';
< xchr[@'137]←'_';@/
< xchr[@'140]←'`';
< xchr[@'141]←'a';
< xchr[@'142]←'b';
< xchr[@'143]←'c';
< xchr[@'144]←'d';
< xchr[@'145]←'e';
< xchr[@'146]←'f';
< xchr[@'147]←'g';@/
< xchr[@'150]←'h';
< xchr[@'151]←'i';
< xchr[@'152]←'j';
< xchr[@'153]←'k';
< xchr[@'154]←'l';
< xchr[@'155]←'m';
< xchr[@'156]←'n';
< xchr[@'157]←'o';@/
< xchr[@'160]←'p';
< xchr[@'161]←'q';
< xchr[@'162]←'r';
< xchr[@'163]←'s';
< xchr[@'164]←'t';
< xchr[@'165]←'u';
< xchr[@'166]←'v';
< xchr[@'167]←'w';@/
< xchr[@'170]←'x';
< xchr[@'171]←'y';
< xchr[@'172]←'z';
< xchr[@'173]←'{';
< xchr[@'174]←'|';
< xchr[@'175]←'}';
< xchr[@'176]←'~';@/
< xchr[0]←' '; xchr[@'177]←' ';
< {ascii codes 0 and |@'177| do not appear in text}
---
> xchr[@'40]:=' ';
> xchr[@'41]:='!';
> xchr[@'42]:='"';
> xchr[@'43]:='#';
> xchr[@'44]:='$';
> xchr[@'45]:='%';
> xchr[@'46]:='&';
> xchr[@'47]:='''';@/
> xchr[@'50]:='(';
> xchr[@'51]:=')';
> xchr[@'52]:='*';
> xchr[@'53]:='+';
> xchr[@'54]:=',';
> xchr[@'55]:='-';
> xchr[@'56]:='.';
> xchr[@'57]:='/';@/
> xchr[@'60]:='0';
> xchr[@'61]:='1';
> xchr[@'62]:='2';
> xchr[@'63]:='3';
> xchr[@'64]:='4';
> xchr[@'65]:='5';
> xchr[@'66]:='6';
> xchr[@'67]:='7';@/
> xchr[@'70]:='8';
> xchr[@'71]:='9';
> xchr[@'72]:=':';
> xchr[@'73]:=';';
> xchr[@'74]:='<';
> xchr[@'75]:='=';
> xchr[@'76]:='>';
> xchr[@'77]:='?';@/
> xchr[@'100]:='@@';
> xchr[@'101]:='A';
> xchr[@'102]:='B';
> xchr[@'103]:='C';
> xchr[@'104]:='D';
> xchr[@'105]:='E';
> xchr[@'106]:='F';
> xchr[@'107]:='G';@/
> xchr[@'110]:='H';
> xchr[@'111]:='I';
> xchr[@'112]:='J';
> xchr[@'113]:='K';
> xchr[@'114]:='L';
> xchr[@'115]:='M';
> xchr[@'116]:='N';
> xchr[@'117]:='O';@/
> xchr[@'120]:='P';
> xchr[@'121]:='Q';
> xchr[@'122]:='R';
> xchr[@'123]:='S';
> xchr[@'124]:='T';
> xchr[@'125]:='U';
> xchr[@'126]:='V';
> xchr[@'127]:='W';@/
> xchr[@'130]:='X';
> xchr[@'131]:='Y';
> xchr[@'132]:='Z';
> xchr[@'133]:='[';
> xchr[@'134]:='\';
> xchr[@'135]:=']';
> xchr[@'136]:='^';
> xchr[@'137]:='_';@/
> xchr[@'140]:='`';
> xchr[@'141]:='a';
> xchr[@'142]:='b';
> xchr[@'143]:='c';
> xchr[@'144]:='d';
> xchr[@'145]:='e';
> xchr[@'146]:='f';
> xchr[@'147]:='g';@/
> xchr[@'150]:='h';
> xchr[@'151]:='i';
> xchr[@'152]:='j';
> xchr[@'153]:='k';
> xchr[@'154]:='l';
> xchr[@'155]:='m';
> xchr[@'156]:='n';
> xchr[@'157]:='o';@/
> xchr[@'160]:='p';
> xchr[@'161]:='q';
> xchr[@'162]:='r';
> xchr[@'163]:='s';
> xchr[@'164]:='t';
> xchr[@'165]:='u';
> xchr[@'166]:='v';
> xchr[@'167]:='w';@/
> xchr[@'170]:='x';
> xchr[@'171]:='y';
> xchr[@'172]:='z';
> xchr[@'173]:='{';
> xchr[@'174]:='|';
> xchr[@'175]:='}';
> xchr[@'176]:='~';@/
563c608
< @ Some of the ascii codes without visible characters have been given symbolic
---
> @ Some of the ASCII codes without visible characters have been given symbolic
566,568c611,613
< @d null_code=@'0 {ascii code that might disappear}
< @d carriage_return=@'15 {ascii code used at end of line}
< @d invalid_code=@'177 {ascii code that should not appear}
---
> @d null_code=@'0 {ASCII code that might disappear}
> @d carriage_return=@'15 {ASCII code used at end of line}
> @d invalid_code=@'177 {ASCII code that many systems prohibit in text files}
570c615
< @ The ascii code is ``standard'' only to a certain extent, since many
---
> @ The ASCII code is ``standard'' only to a certain extent, since many
572c617
< to more than 94 printing characters. Appendix@@C of the \TeX\ manual
---
> to more than 94 printing characters. Appendix~C of {\sl The \TeX book\/}
574a620
> @:TeXbook}{\sl The \TeX book@>
577c623
< on a garden-variety \PASCAL\ for which only standard ascii
---
> on a garden-variety \PASCAL\ for which only standard ASCII
579c625
< what codes are specified in |xchr[1..@'37]|, but the safest policy is to
---
> what codes are specified in |xchr[0..@'37]|, but the safest policy is to
584,588c630
< like `\.\NE' instead of `\.{\\ne}'. At MIT, for example, it would be more
< appropriate to substitute the code
< $$\hbox{|for i←1 to @'37 do xchr[i]←chr(i);|}$$
< \TeX's character set is essentially the same as MIT's, even with respect to
< characters less than@@@'40. People with extended character sets can
---
> like `\.^^Z' instead of `\.{\\ne}'. People with extended character sets can
590,596c632,638
< characters the users of \TeX\ are allowed to have in their input files,
< provided that unsuitable characters do not correspond to the special
< codes like |carriage_return| that are listed above. It is best
< to make the codes correspond to the intended interpretations as shown
< in Appendix@@C whenever possible; but this is not necessary. For example,
< in countries with an alphabet of more than 26 letters, it is usually best
< to map the additional letters into codes less than@@@'40.
---
> characters the users of \TeX\ are allowed to have in their input files.
> It is best to make the codes correspond to the intended interpretations as
> shown in Appendix~C whenever possible; but this is not necessary. For
> example, in countries with an alphabet of more than 26 letters, it is
> usually best to map the additional letters into codes less than~@'40.
> To get the most ``permissive'' character set, change |' '| on the
> right of these assignment statements to |chr(i)|.
601c643,644
< for i←1 to @'37 do xchr[i]←' ';
---
> for i:=0 to @'37 do xchr[i]:=' ';
> for i:=@'177 to @'377 do xchr[i]:=' ';
606c649
< |j| or more; hence, standard ascii code numbers will be used instead of
---
> |j| or more; hence, standard ASCII code numbers will be used instead of
610,611c653,656
< for i←first_text_char to last_text_char do xord[chr(i)]←invalid_code;
< for i←1 to @'176 do xord[xchr[i]]←i;
---
> for i:=first_text_char to last_text_char do xord[chr(i)]:=invalid_code;
> for i:=@'200 to @'377 do xord[xchr[i]]:=i;
> for i:=0 to @'176 do xord[xchr[i]]:=i;
>
616c661
< that input and output are not a part of ``real'' programming. Well, it is true
---
> that input and output are not part of ``real'' programming. Well, it is true
621,622c666,667
< two choices: whether to attack I/O now and get it over with, or to postpone
< it until near the end. Neither prospect is very attractive, so let's
---
> two choices, either to attack I/O now and get it over with, or to postpone
> I/O until near the end. Neither prospect is very attractive, so let's
625,627c670,672
< The basic operations we need to do are (1)@@inputting and outputting of
< text, to or from a file or the user's terminal; (2)@@inputting and
< outputting of eight-bit bytes, to or from a file; (3)@@instructing the
---
> The basic operations we need to do are (1)~inputting and outputting of
> text, to or from a file or the user's terminal; (2)~inputting and
> outputting of eight-bit bytes, to or from a file; (3)~instructing the
629c674
< output from a specified file; (4)@@testing whether the end of an input
---
> output from a specified file; (4)~testing whether the end of an input
632c677
< Note that \TeX\ needs to deal with only two kinds of files.
---
> \TeX\ needs to deal with two kinds of files.
641,644c686,689
< \TeX\ actually makes use also of a third kind of file, called a |word_file|,
< when dumping and reloading format information for its own initialization.
< We shall define a word file later; but it will be possible for us to
< do a few simple things with them in this section before they are defined.
---
> The program actually makes use also of a third kind of file, called a
> |word_file|, when dumping and reloading base information for its own
> initialization. We shall define a word file later; but it will be possible
> for us to specify simple operations on word files before they are defined.
664,667c709,712
< @!name_of_file:packed array[1..file_name_size] of char;
< {on some systems this may be a \&{record} variable}
< @!name_length:0..file_name_size; {this many characters are actually
< relevant in |name_of_file| (the rest are blank)}
---
> @!name_of_file:packed array[1..file_name_size] of char;@;@/
> {on some systems this may be a \&{record} variable}
> @!name_length:0..file_name_size;@/{this many characters are actually
> relevant in |name_of_file| (the rest are blank)}
669,670c714,715
< @ The \ph\ compiler with which the present version of \TeX\ was pre\-pared has
< extended the rules of \PASCAL\ in a very convenient way. To open file@@|f|,
---
> @ The \ph\ compiler with which the present version of \TeX\ was prepared has
> extended the rules of \PASCAL\ in a very convenient way. To open file~|f|,
672,677c717,722
< $$\vbox{\halign{#\hfil\qquadⓧ#\hfil\cr
< |reset(f,@t\\{name}@>,'/O')|ⓧfor input;\cr
< |rewrite(f,@t\\{name}@>,'/O')|ⓧfor output.\cr}}$$
< The `\\{name}' parameter, which is of type `\!|packed
< array[@t\<\\{any}>@>] of text_char|', stands for the name of
< the external file that is being opened for input or output.
---
> $$\vbox{\halign{#\hfil\qquad&#\hfil\cr
> |reset(f,@t\\{name}@>,'/O')|&for input;\cr
> |rewrite(f,@t\\{name}@>,'/O')|&for output.\cr}}$$
> The `\\{name}' parameter, which is of type `{\bf packed array
> $[\langle\\{any}\rangle]$ of \\{char}}', stands for the name of
> the external file that is being opened for input or output.
683,685c728,730
< (e.g., it might already be in use), we will have |eof(f)=true| after an
< unsuccessful |reset|, and |eof(f)=false| after an unsuccessful |rewrite|.
< This allows \TeX\ to undertake appropriate corrective action.
---
> (e.g., someone may already be trying to write the same file), we will have
> |@!erstat(f)<>0| after an unsuccessful |reset| or |rewrite|. This allows
> \TeX\ to undertake appropriate corrective action.
686a732
> @^system dependencies@>
688,690c734,735
< We can now implement the file-opening procedures in the following simple way,
< where the functions return |false| if no file identified by |name_of_file|
< could be opened:
---
> \TeX's file-opening procedures return |false| if no file identified by
> |name_of_file| could be opened.
691a737,738
> @d reset_OK(#)==erstat(#)=0
> @d rewrite_OK(#)==erstat(#)=0
694,695c741,742
< {open a text file for input}
< begin reset(f,name_of_file,'/O'); a_open_in←not eof(f);
---
> {open a text file for input}
> begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f);
699,700c746,747
< {open a text file for output}
< begin rewrite(f,name_of_file,'/O'); a_open_out←eof(f);
---
> {open a text file for output}
> begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f);
704,705c751,752
< {open a binary file for input}
< begin reset(f,name_of_file,'/O'); b_open_in←not eof(f);
---
> {open a binary file for input}
> begin reset(f,name_of_file,'/O'); b_open_in:=reset_OK(f);
709,710c756,757
< {open a binary file for output}
< begin rewrite(f,name_of_file,'/O'); b_open_out←eof(f);
---
> {open a binary file for output}
> begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f);
714,715c761,762
< {open a word file for input}
< begin reset(f,name_of_file,'/O'); w_open_in←not eof(f);
---
> {open a word file for input}
> begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f);
719,720c766,767
< {open a word file for output}
< begin rewrite(f,name_of_file,'/O'); w_open_out←eof(f);
---
> {open a word file for output}
> begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f);
723a771
> @:PASCAL H}{\ph@>
729a778,780
> These procedures should not generate error messages if a file is
> being closed before it has been successfully opened.
>
744c795
< binary@@I/O. Text output is also easy to do with standard \PASCAL\ routines.
---
> binary~I/O. Text output is also easy to do with standard \PASCAL\ routines.
746,747c797,798
< of the necessary translation to |ascii_code| values, and because
< \TeX's conventions should be efficient and they should
---
> of the necessary translation to |ASCII_code| values.
> \TeX's conventions should be efficient, and they should
753c804
< now, it suffices for us to know that |buffer| is an array of |ascii_code|
---
> now, it suffices for us to know that |buffer| is an array of |ASCII_code|
758c809
< @!buffer:array[0..buf_size] of ascii_code; {lines of characters being read}
---
> @!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read}
764,769c815,822
< field into available positions of the buffer array and returns the value |true|,
< unless the file has already been entirely read, in which case it returns
< |false| (and does nothing else). The |ascii_code| numbers that represent
< the next line of the file are input into |buffer[first]|, |buffer[first+1]|,
< $\ldotss$, |buffer[last-1]|; and the global variable |last| is set equal
< to |first| plus the length of the line.
---
> file into available positions of the buffer array and returns the value
> |true|, unless the file has already been entirely read, in which case it
> returns |false| and sets |last:=first|. In general, the |ASCII_code|
> numbers that represent the next line of the file are input into
> |buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the
> global variable |last| is set equal to |first| plus the length of the
> line. Trailing blanks are removed from the line; thus, either |last=first|
> (in which case the line was entirely blank) or |buffer[last-1]<>" "|.
772c825
< would make |last≥buf_size|; this is done so that other parts of \TeX\
---
> would make |last>=buf_size|; this is done so that other parts of \TeX\
775,776c828,829
< |first<buf_size| will always hold, so there is always room for an ``empty''
< line.
---
> |first<buf_size| will always hold, so that there is always room for an
> ``empty'' line.
782,786c835,844
< This procedure does a |get| before looking at the first character of the
< line, and it does not do a |get| when it reaches the end of the line.
< Therefore it can be used to acquire input from the user's terminal as well
< as from ordinary text files. Other parts of \TeX\ take care of inputting
< the first line of a file, so that the first character is not lost.
---
> If the |bypass_eoln| parameter is |true|, |input_ln| will do a |get|
> before looking at the first character of the line; this skips over
> an |eoln| that was in |f^|. The procedure does not do a |get| when it
> reaches the end of the line; therefore it can be used to acquire input
> from the user's terminal as well as from ordinary text files.
>
> Standard \PASCAL\ says that a file should have |eoln| immediately
> before |eof|, but \TeX\ needs only a weaker restriction: If |eof|
> occurs in the middle of a line, the system function |eoln| should return
> a |true| result (even though |f^| will be undefined).
796,810c854,872
< @p function input_ln(var f:alpha_file):boolean; {inputs the next line
< or returns |false|}
< begin get(f); {input the first character of the line into |f^|}
< if eof(f) then input_ln←false
< else begin last←first; {cf.\ Matthew 19:30}
< while not eoln(f) do
< begin if last≥max_buf_stack then
< begin max_buf_stack←last+1;
< if max_buf_stack=buf_size then
< overflow("buffer size",buf_size);
< end;
< buffer[last]←xord[f^]; get(f); incr(last);
< end;
< input_ln←true;
< end;
---
> @p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean;
> {inputs the next line or returns |false|}
> var last_nonblank:0..buf_size; {|last| with trailing blanks removed}
> begin if bypass_eoln then if not eof(f) then get(f);
> {input the first character of the line into |f^|}
> last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
> if eof(f) then input_ln:=false
> else begin last_nonblank:=first;
> while not eoln(f) do
> begin if last>=max_buf_stack then
> begin max_buf_stack:=last+1;
> if max_buf_stack=buf_size then
> @<Report overflow of the input buffer, and abort@>;
> end;
> buffer[last]:=xord[f^]; get(f); incr(last);
> if buffer[last-1]<>" " then last_nonblank:=last;
> end;
> last:=last_nonblank; input_ln:=true;
> end;
823c885,887
< @ Here is how to open the terminal files in \ph:
---
> @ Here is how to open the terminal files
> in \ph. The `\.{/I}' switch suppresses the first |get|.
> @:PASCAL H}{\ph@>
826c890
< @d t_open_in==reset(term_in,'TTY:','/O') {open the terminal for text input}
---
> @d t_open_in==reset(term_in,'TTY:','/O/I') {open the terminal for text input}
830c894,895
< happens on the user's terminal, and two procedures are used for this
---
> happens on the user's terminal, and three system-dependent
> procedures are used for this
834c899
< The other, |clear_terminal|, is called when we wish to cancel any
---
> The second, |clear_terminal|, is called when we wish to cancel any
836,837c901,905
< issue an unexpected error message). The following macros show how these
< two operations can be specified in \ph:
---
> issue an unexpected error message). The third, |wake_up_terminal|,
> is supposed to revive the terminal if the user has disabled it by
> some instruction to the operating system. The following macros show how
> these operations can be specified in \ph:
> @:PASCAL H}{\ph@>
841a910
> @d wake_up_terminal == do_nothing {cancel the user's cancellation of output}
844,845c913,914
< the user's terminal. This line is special because it is read before we
< have opened the error transcript file; there is sort of a ``chicken and
---
> the user's terminal. This line is different because it is read before we
> have opened the transcript file; there is sort of a ``chicken and
848c917
< the transcript file will be named `\.{paper.err}'; but if no \.{\\input}
---
> the transcript file will be named `\.{paper.log}'; but if no \.{\\input}
850c919
< file will acquire its default name `\.{texput.err}'. (The transcript file
---
> file will acquire its default name `\.{texput.log}'. (The transcript file
852a922
> @.texput@>
862,863c932,951
< @ Different systems have different ways to get started, but regardless of
< what conventions are adopted the routine that initializes the terminal
---
> The first line is special also because it may be read before \TeX\ has
> input a format file. In such cases, normal error messages cannot yet
> be given. The following code uses concepts that will be explained later.
> (If the \PASCAL\ compiler does not support non-local |@!goto|\unskip, the
> @^system dependencies@>
> statement `|goto final_end|' should be replaced by something that
> quietly terminates the program.)
>
> @<Report overflow of the input buffer, and abort@>=
> if format_ident=0 then
> begin write_ln(term_out,'Buffer size exceeded!'); goto final_end;
> @.Buffer size exceeded@>
> end
> else begin cur_input.loc_field:=first; cur_input.limit_field:=last-1;
> overflow("buffer size",buf_size);
> @:TeX capacity exceeded buffer size}{\quad buffer size@>
> end
>
> @ Different systems have different ways to get started. But regardless of
> what conventions are adopted, the routine that initializes the terminal
867,868c955,956
< terminal. (The file |term_out| will already be open for output to the
< terminal.)
---
> terminal. (The file |term_out| will already be open for output to the
> terminal.)
871,873c959,961
< considered the first line of terminal input. Otherwise the
< user should be prompted with `\.{**}', and the first line of input
< should be whatever is typed in response.
---
> considered the first line of terminal input. Otherwise the
> user should be prompted with `\.{**}', and the first line of input
> should be whatever is typed in response.
876,877c964,965
< command line, should appear in locations 0 to |last-1| of the
< |buffer| array.
---
> command line, should appear in locations |first| to |last-1| of the
> |buffer| array.
880,881c968,969
< character that \TeX\ reads next is in |buffer[loc]|. This
< character should not be blank, and we should have |loc<last|.
---
> character to be read next by \TeX\ is in |buffer[loc]|. This
> character should not be blank, and we should have |loc<last|.
884,886c972,974
< before a non-blank line comes in. The prompt is `\.\#' instead of the
< later `\.*' because the meaning is slightly different: `\\input' need
< not be typed immediately after `\.\#'.)
---
> before a non-blank line comes in. The prompt is `\.{**}' instead of the
> later `\.*' because the meaning is slightly different: `\.{\\input}' need
> not be typed immediately after~`\.{**}'.)
891c979
< with\-out retrieving a possible command line.
---
> without retrieving a possible command line.
899,913c987,1002
< loop@+begin write(term_out,'**'); update_terminal;
< if not input_ln(term_in) then {this shouldn't happen}
< begin write_ln(term_out);
< write(term_out,'! End of file on the terminal... why?');
< @.End of file on the terminal...@>
< init_terminal←false; return;
< end;
< loc←first;
< while (loc<last)∧(buffer[loc]=" ") do incr(loc);
< if loc<last then
< begin init_terminal←true;
< return; {return unless the line was all blank}
< end;
< write_ln(term_out,'Please type the name of your input file.');
< end;
---
> loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal;
> @.**@>
> if not input_ln(term_in,true) then {this shouldn't happen}
> begin write_ln(term_out);
> write(term_out,'! End of file on the terminal... why?');
> @.End of file on the terminal@>
> init_terminal:=false; return;
> end;
> loc:=first;
> while (loc<last)and(buffer[loc]=" ") do incr(loc);
> if loc<last then
> begin init_terminal:=true;
> return; {return unless the line was all blank}
> end;
> write_ln(term_out,'Please type the name of your input file.');
> end;
914a1004
>
917c1007
< of seven-bit characters. Since \PASCAL\ does not have a well-developed string
---
> of eight-bit characters. Since \PASCAL\ does not have a well-developed string
922c1012
< The array |str_pool| contains all of the (seven-bit) ascii codes in all
---
> The array |str_pool| contains all of the (eight-bit) ASCII codes in all
925,926c1015,1016
< string number |s| comprises the characters |str_pool[j]| for
< |str_start[s]≤j<str_start[s+1]|. Additional integer variables
---
> string number |s| comprises the characters |str_pool[j]| for
> |str_start[s]<=j<str_start[s+1]|. Additional integer variables
932,933c1022,1023
< String numbers 0 to 127 are reserved for strings that correspond to single
< ascii characters. This is in accordance with the conventions of \.{WEB},
---
> String numbers 0 to 255 are reserved for strings that correspond to single
> ASCII characters. This is in accordance with the conventions of \.{WEB},
935c1025
< which converts single-character strings into the ascii code number of the
---
> which converts single-character strings into the ASCII code number of the
939,941c1029,1031
< ascii code for a period, while \.{WEB} will convert a string like \.{"hello"}
< into some integer greater than@@127. String number 46 will presumably be the
< single character `\..'; but some ascii codes have no standard visible
---
> ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"}
> into some integer greater than~255. String number 46 will presumably be the
> single character `\..'; but some ASCII codes have no standard visible
943,944c1033,1044
< ascii character, so the first 128 strings are used to specify exactly what
< should be printed for each of the 128 possibilities.
---
> ASCII character, so the first 256 strings are used to specify exactly what
> should be printed for each of the 256 possibilities.
>
> Elements of the |str_pool| array must be ASCII codes that can actually
> be printed; i.e., they must have an |xchr| equivalent in the local
> character set. (This restriction applies only to preloaded strings,
> not to those generated dynamically by the user.)
>
> Some \PASCAL\ compilers won't pack integers into a single byte unless the
> integers lie in the range |-128..127|. To accommodate such systems
> we access the string pool only via macros that can easily be redefined.
> @^system dependencies@>
946,948c1046,1047
< Elements of the |str_pool| array must be ascii codes that can actually be
< printed; i.e., they must have an |xchr| equivalent in the local
< character set.
---
> @d si(#) == # {convert from |ASCII_code| to |packed_ASCII_code|}
> @d so(#) == # {convert from |packed_ASCII_code| to |ASCII_code|}
952a1052
> @!packed_ASCII_code = 0..255; {elements of |str_pool| array}
954,955c1054,1055
< @ @<Globals...@>=
< @!str_pool:packed array[pool_pointer] of ascii_code; {the characters}
---
> @ @<Glob...@>=
> @!str_pool:packed array[pool_pointer] of packed_ASCII_code; {the characters}
958c1058,1060
< @!str_ptr : str_number; {start of the current string being created}
---
> @!str_ptr : str_number; {number of the current string being created}
> @!init_pool_ptr : pool_pointer; {the starting value of |pool_ptr|}
> @!init_str_ptr : str_number; {the starting value of |str_ptr|}
961c1063
< macros instead of using \PASCAL\ procedures, because many of the
---
> macros instead of \PASCAL\ procedures, because many of the
968c1070
< in string number \#}
---
> in string number \#}
975c1077
< The macro called |append_char|, defined here, does not check to see if the
---
> The |append_char| macro, defined here, does not check to see if the
984,985c1086,1087
< @d append_char(#) == {put |ascii_code| \#\ at the end of |str_pool|}
< begin str_pool[pool_ptr]←#; incr(pool_ptr);
---
> @d append_char(#) == {put |ASCII_code| \# at the end of |str_pool|}
> begin str_pool[pool_ptr]:=si(#); incr(pool_ptr);
988,990c1090,1094
< @d str_room(#) == begin if pool_ptr+# > pool_size then
< overflow("pool size",pool_size);
< end
---
> @d str_room(#) == {make sure that the pool hasn't overflowed}
> begin if pool_ptr+# > pool_size then
> overflow("pool size",pool_size-init_pool_ptr);
> @:TeX capacity exceeded pool size}{\quad pool size@>
> end
999,1001c1103,1106
< overflow("number of strings",max_strings);
< incr(str_ptr); str_start[str_ptr]←pool_ptr;
< make_string←str_ptr-1;
---
> overflow("number of strings",max_strings-init_str_ptr);
> @:TeX capacity exceeded number of strings}{\quad number of strings@>
> incr(str_ptr); str_start[str_ptr]:=pool_ptr;
> make_string:=str_ptr-1;
1006,1007c1111,1112
< @d flush_string==begin decr(str_ptr); pool_ptr←str_start[str_ptr];
< end
---
> @d flush_string==begin decr(str_ptr); pool_ptr:=str_start[str_ptr];
> end
1011a1117,1118
> Empirical tests indicate that |str_eq_buf| is used in such a way that
> it tends to return |true| about 80 percent of the time.
1014c1121
< {test equality of strings}
---
> {test equality of strings}
1018c1125
< begin j←str_start[s]; result←false;
---
> begin j:=str_start[s];
1020,1024c1127,1133
< begin if str_pool[j]≠buffer[k] then goto not_found;
< incr(j); incr(k);
< end;
< result←true;
< not_found: str_eq_buf←result;
---
> begin if so(str_pool[j])<>buffer[k] then
> begin result:=false; goto not_found;
> end;
> incr(j); incr(k);
> end;
> result:=true;
> not_found: str_eq_buf:=result;
1031c1140
< {test equality of strings}
---
> {test equality of strings}
1035,1037c1144,1146
< begin result←false;
< if length(s)≠length(t) then goto not_found;
< j←str_start[s]; k←str_start[t];
---
> begin result:=false;
> if length(s)<>length(t) then goto not_found;
> j:=str_start[s]; k:=str_start[t];
1039,1043c1148,1152
< begin if str_pool[j]≠str_pool[k] then goto not_found;
< incr(j); incr(k);
< end;
< result←true;
< not_found: str_eq_str←result;
---
> begin if str_pool[j]<>str_pool[k] then goto not_found;
> incr(j); incr(k);
> end;
> result:=true;
> not_found: str_eq_str:=result;
1052,1053c1161,1162
< @p init function init_strings:boolean; {initializes the string pool, but
< returns |false| if something goes wrong}
---
> @p @!init function get_strings_started:boolean; {initializes the string pool,
> but returns |false| if something goes wrong}
1055c1164
< var k,@!l:0..127; {small indices or counters}
---
> var k,@!l:0..255; {small indices or counters}
1059,1061c1168,1170
< @!c:boolean; {check sum has checked}
< begin pool_ptr←0; str_ptr←0;
< @<Make the first 128 strings@>;
---
> @!c:boolean; {check sum has been checked}
> begin pool_ptr:=0; str_ptr:=0; str_start[0]:=0;
> @<Make the first 256 strings@>;
1063c1172
< or give an error message and return |false|@>;
---
> or give an error message and return |false|@>;
1067,1079c1176,1190
< @ @<Make the first 128...@>=
< for k←0 to 127 do
< begin if (k<" ")∧(@<Character |k| cannot be printed@>) then
< begin append_char("^"); append_char("^");
< append_char(k+@'100);
< end
< else if k=127 then
< begin append_char("^"); append_char("^");
< append_char("?");
< end
< else append_char(k);
< g←make_string;
< end
---
> @ @d app_lc_hex(#)==l:=#;
> if l<10 then append_char(l+"0")@+else append_char(l-10+"a")
>
> @<Make the first 256...@>=
> for k:=0 to 255 do
> begin if (@<Character |k| cannot be printed@>) then
> begin append_char("^"); append_char("^");
> if k<@'100 then append_char(k+@'100)
> else if k<@'200 then append_char(k-@'100)
> else begin app_lc_hex(k div 16); app_lc_hex(k mod 16);
> end;
> end
> else append_char(k);
> g:=make_string;
> end
1081c1192
< @ The first 128 strings will contain 95 standard ascii characters, and the
---
> @ The first 128 strings will contain 95 standard ASCII characters, and the
1084c1195
< an extended character set, where for example |xchr[@'32]=@t\.{\'\NE\'}@>|,
---
> an extended character set, where for example |xchr[@'32]=@t\.{\'^^Z\'}@>|,
1092,1096c1203,1215
< The boolean expression defined here should be |true| unless \TeX\ internal code
< number@@$k$ corresponds to a non-troublesome visible symbol in the local
< char\-ac\-ter set, given that |k<@'40|.
< At MIT, for example, the appropriate formula would be
< `|k in [0,@'10..@'12,@'14,@'15,@'33]|'.
---
> Unprintable characters of codes 128--255 are, similarly, rendered
> \.{\^\^80}--\.{\^\^ff}.
>
> The boolean expression defined here should be |true| unless \TeX\
> internal code number~|k| corresponds to a non-troublesome visible
> symbol in the local character set. An appropriate formula for the
> extended character set recommended in {\sl The \TeX book\/} would, for
> example, be `|k in [0,@'10..@'12,@'14,@'15,@'33,@'177..@'377]|'.
> If character |k| cannot be printed, and |k<@'200|, then character |k+@'100| or
> |k-@'100| must be printable; moreover, ASCII codes |[@'41..@'46,
> @'60..@'71, @'136, @'141..@'146, @'160..@'171]| must be printable.
> Thus, at least 81 printable characters are needed.
> @:TeXbook}{\sl The \TeX book@>
1101c1220
< true
---
> (k<" ")or(k>"~")
1104c1223
< de\-scription that you are now reading, it outputs the \PASCAL\ program
---
> description that you are now reading, it outputs the \PASCAL\ program
1112c1231,1232
< @!pool_file:alpha_file; {the string-pool file output by \.{TANGLE}}
---
> @!init @!pool_file:alpha_file; {the string-pool file output by \.{TANGLE}}
> tini
1114,1115c1234,1236
< @ @d bad_pool(#)==begin write_ln(term_out,#); init_strings←false; return;
< end
---
> @ @d bad_pool(#)==begin wake_up_terminal; write_ln(term_out,#);
> a_close(pool_file); get_strings_started:=false; return;
> end
1117c1238
< name_of_file←pool_name; {we needn't set |name_length|}
---
> name_of_file:=pool_name; {we needn't set |name_length|}
1119,1125c1240,1246
< begin c←false;
< repeat @<Read one string, but return |false| if the
< string memory space is getting too tight for comfort@>;
< until c;
< init_strings←true;
< end
< else bad_pool('! I can''t read TEX.POOL.')
---
> begin c:=false;
> repeat @<Read one string, but return |false| if the
> string memory space is getting too tight for comfort@>;
> until c;
> a_close(pool_file); get_strings_started:=true;
> end
> else bad_pool('! I can''t read TEX.POOL.')
1133,1135c1254,1256
< else begin if (xord[m]<"0")∨(xord[m]>"9")∨@|
< (xord[n]<"0")∨(xord[n]>"9") then
< bad_pool('! TEX.POOL line doesn''t begin with two digits.');
---
> else begin if (xord[m]<"0")or(xord[m]>"9")or@|
> (xord[n]<"0")or(xord[n]>"9") then
> bad_pool('! TEX.POOL line doesn''t begin with two digits.');
1137,1139c1258,1260
< l←xord[m]*10+xord[n]-"0"*11; {compute the length}
< if pool_ptr+l+string_vacancies>pool_size then
< bad_pool('! You have to increase POOLSIZE.');
---
> l:=xord[m]*10+xord[n]-"0"*11; {compute the length}
> if pool_ptr+l+string_vacancies>pool_size then
> bad_pool('! You have to increase POOLSIZE.');
1141,1147c1262,1267
< for k←1 to l do
< begin if eoln(pool_file) then bad_pool('! TEX.POOL line too short.');
< @.TEX.POOL line too short@>
< read(pool_file,m); append_char(xord[m]);
< end;
< read_ln(pool_file); g←make_string;
< end;
---
> for k:=1 to l do
> begin if eoln(pool_file) then m:=' '@+else read(pool_file,m);
> append_char(xord[m]);
> end;
> read_ln(pool_file); g:=make_string;
> end;
1150c1270
< @ The \.{WEB} operation \.{\at\$} denotes the value that should be at the
---
> @ The \.{WEB} operation \.{@@\$} denotes the value that should be at the
1156,1158c1276,1278
< begin a←0; k←1;
< loop@+ begin if (xord[n]<"0")∨(xord[n]>"9") then
< bad_pool('! TEX.POOL check sum doesn''t have nine digits.');
---
> begin a:=0; k:=1;
> loop@+ begin if (xord[n]<"0")or(xord[n]>"9") then
> bad_pool('! TEX.POOL check sum doesn''t have nine digits.');
1160,1164c1280,1284
< a←10*a+xord[n]-"0";
< if k=9 then goto done;
< incr(k); read(pool_file,n);
< end;
< done: if a≠@$ then bad_pool('! TEX.POOL doesn''t match; TANGLE me again.');
---
> a:=10*a+xord[n]-"0";
> if k=9 then goto done;
> incr(k); read(pool_file,n);
> end;
> done: if a<>@$ then bad_pool('! TEX.POOL doesn''t match; TANGLE me again.');
1166c1286
< c←true;
---
> c:=true;
1167a1288
>
1169c1290
< Messages that are sent to a user's terminal and to the error-transcript file
---
> Messages that are sent to a user's terminal and to the transcript-log file
1176,1177c1297,1298
< \hang |term_and_err|, the normal setting, prints on the terminal and on the
< error-transcript file.
---
> \hang |term_and_log|, the normal setting, prints on the terminal and on the
> transcript file.
1179c1300
< \hang |err_only|, prints only on the error-transcript file.
---
> \hang |log_only|, prints only on the transcript file.
1184c1305
< before the error-transcript file is open.
---
> before the transcript file is open.
1186,1188c1307,1309
< \hang |pseudo|, puts output into a cyclic buffer that is used
< by the |show_context| routine; see that routine for the
< reasoning behind this curious mode.
---
> \hang |pseudo|, puts output into a cyclic buffer that is used
> by the |show_context| routine; when we get to that routine we shall discuss
> the reasoning behind this curious mode.
1191c1312
< string pool.
---
> string pool.
1193c1314
< \hang 0 to 15, prints on one of the sixteen files for \.{\\send} output.
---
> \hang 0 to 15, prints on one of the sixteen files for \.{\\write} output.
1196c1317
< \noindent The symbolic names `|term_and_err|', etc., have been assigned
---
> \noindent The symbolic names `|term_and_log|', etc., have been assigned
1198c1319
< |no_print+2=err_only|, |term_only+2=err_only+1=term_and_err|.
---
> |no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|.
1200,1205c1321,1327
< Two additional global variables, |tally| and |offset|, record
< the number of characters that have been printed since they were most
< recently cleared to zero. We use |tally| to record the length of
< (possibly very long) stretches of printing; |offset|, on the other
< hand, keeps track of how many characters have appeared so far on the current
< line of text output.
---
> Three additional global variables, |tally| and |term_offset| and
> |file_offset|, record the number of characters that have been printed
> since they were most recently cleared to zero. We use |tally| to record
> the length of (possibly very long) stretches of printing; |term_offset|
> and |file_offset|, on the other hand, keep track of how many characters
> have appeared so far on the current line that has been output to the
> terminal or to the transcript file, respectively.
1209,1210c1331,1332
< @d err_only=18 {printing is destined for the transcript file only}
< @d term_and_err=19 {normal |selector| setting}
---
> @d log_only=18 {printing is destined for the transcript file only}
> @d term_and_log=19 {normal |selector| setting}
1215,1216c1337,1338
< @<Globals...@>=
< @!err_file : alpha_file; {transcript of \TeX\ session}
---
> @<Glob...@>=
> @!log_file : alpha_file; {transcript of \TeX\ session}
1218c1340
< @!dig : array[0..22] of 0..9; {digits in a number being output}
---
> @!dig : array[0..22] of 0..15; {digits in a number being output}
1220,1222c1342,1347
< @!offset : 0..max_print_line; {the number of characters on the current line}
< @!trick_buf:array[0..error_line] of ascii_code; {circular buffer for
< pseudoprinting}
---
> @!term_offset : 0..max_print_line;
> {the number of characters on the current terminal line}
> @!file_offset : 0..max_print_line;
> {the number of characters on the current file line}
> @!trick_buf:array[0..error_line] of ASCII_code; {circular buffer for
> pseudoprinting}
1227c1352,1365
< selector←term_only; tally←0; offset←0;
---
> selector:=term_only; tally:=0; term_offset:=0; file_offset:=0;
>
> @ Macro abbreviations for output to the terminal and to the log file are
> defined here for convenience. Some systems need special conventions
> for terminal output, and it is possible to adhere to those conventions
> by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section.
> @^system dependencies@>
>
> @d wterm(#)==write(term_out,#)
> @d wterm_ln(#)==write_ln(term_out,#)
> @d wterm_cr==write_ln(term_out)
> @d wlog(#)==write(log_file,#)
> @d wlog_ln(#)==write_ln(log_file,#)
> @d wlog_cr==write_ln(log_file)
1233d1370
< label exit;
1235,1241c1372,1380
< term_and_err: begin write_ln(term_out); write_ln(err_file);
< end;
< err_only: write_ln(err_file);
< term_only: write_ln(term_out);
< no_print: do_nothing;
< pseudo,new_string: return;
< othercases write_ln(send_file[selector])
---
> term_and_log: begin wterm_cr; wlog_cr;
> term_offset:=0; file_offset:=0;
> end;
> log_only: begin wlog_cr; file_offset:=0;
> end;
> term_only: begin wterm_cr; term_offset:=0;
> end;
> no_print,pseudo,new_string: do_nothing;
> othercases write_ln(write_file[selector])
1243,1244c1382
< offset←0; {|tally| is not affected}
< exit:end;
---
> end; {|tally| is not affected}
1250,1251d1387
< @d preserve_offset=90 {go here when |offset| is not to be changed}
<
1253,1258c1389,1410
< procedure print_char(@!c:ascii_code); {prints a single character}
< label preserve_offset;
< begin case selector of
< term_and_err: begin write(term_out,xchr[c]); write(err_file,xchr[c]); end;
< err_only: write(err_file,xchr[c]);
< term_only: write(term_out,xchr[c]);
---
> procedure print_char(@!s:ASCII_code); {prints a single character}
> label exit;
> begin if @<Character |s| is the current new-line character@> then
> if selector<pseudo then
> begin print_ln; return;
> end;
> case selector of
> term_and_log: begin wterm(xchr[s]); wlog(xchr[s]);
> incr(term_offset); incr(file_offset);
> if term_offset=max_print_line then
> begin wterm_cr; term_offset:=0;
> end;
> if file_offset=max_print_line then
> begin wlog_cr; file_offset:=0;
> end;
> end;
> log_only: begin wlog(xchr[s]); incr(file_offset);
> if file_offset=max_print_line then print_ln;
> end;
> term_only: begin wterm(xchr[s]); incr(term_offset);
> if term_offset=max_print_line then print_ln;
> end;
1260,1268c1412,1415
< pseudo: begin if tally<trick_count then
< trick_buf[tally mod error_line]←c;
< goto preserve_offset;
< end;
< new_string: begin if pool_ptr<pool_size then append_char(c);
< goto preserve_offset; {drop characters if the string space is full}
< end;
< othercases begin write(send_file[selector],xchr[c]); goto preserve_offset;
< end
---
> pseudo: if tally<trick_count then trick_buf[tally mod error_line]:=s;
> new_string: begin if pool_ptr<pool_size then append_char(s);
> end; {we drop characters if the string space is full}
> othercases write(write_file[selector],xchr[s])
1270,1273c1417,1418
< incr(offset);
< if offset=max_print_line then print_ln;
< preserve_offset:incr(tally);
< end;
---
> incr(tally);
> exit:end;
1276c1421
< the single standard ascii character \.c, we could call |print("c")|, since
---
> the single standard ASCII character \.c, we could call |print("c")|, since
1279c1424,1426
< routine when it knows that this is safe.
---
> routine when it knows that this is safe. (The present implementation
> assumes that it is always safe to print a visible ASCII character.)
> @^system dependencies@>
1282a1430
> label exit;
1284,1285c1432,1452
< begin if (s<0)∨(s≥str_ptr) then s←0; {this can't happen}
< j←str_start[s];
---
> @!nl:integer; {new-line character to restore}
> begin if s>=str_ptr then s:="???" {this can't happen}
> @.???@>
> else if s<256 then
> if s<0 then s:="???" {can't happen}
> else begin if selector>pseudo then
> begin print_char(s); return; {internal strings are not expanded}
> end;
> if (@<Character |s| is the current new-line character@>) then
> if selector<pseudo then
> begin print_ln; return;
> end;
> nl:=new_line_char; new_line_char:=-1;
> {temporarily disable new-line character}
> j:=str_start[s];
> while j<str_start[s+1] do
> begin print_char(so(str_pool[j])); incr(j);
> end;
> new_line_char:=nl; return;
> end;
> j:=str_start[s];
1287,1288c1454,1470
< begin print_char(str_pool[j]); incr(j);
< end;
---
> begin print_char(so(str_pool[j])); incr(j);
> end;
> exit:end;
>
> @ Control sequence names, file names, and strings constructed with
> \.{\\string} might contain |ASCII_code| values that can't
> be printed using |print_char|. Therefore we use |slow_print| for them:
>
> @<Basic print...@>=
> procedure slow_print(@!s:integer); {prints string |s|}
> var j:pool_pointer; {current character code position}
> begin if (s>=str_ptr) or (s<256) then print(s)
> else begin j:=str_start[s];
> while j<str_start[s+1] do
> begin print(so(str_pool[j])); incr(j);
> end;
> end;
1292c1474
< the version number and format package. The |offset| variable is temporarily
---
> the version number and format package. The |term_offset| variable is temporarily
1298,1301c1480,1484
< write(term_out,banner);
< if format_ident=0 then write_ln(term_out,' (no format preloaded)')
< else begin print(format_ident); print_ln;
< end;
---
> wterm(banner);
> if format_ident=0 then wterm_ln(' (no format preloaded)')
> else begin slow_print(format_ident); print_ln;
> end;
> update_terminal;
1308c1491,1492
< begin if offset>0 then print_ln;
---
> begin if ((term_offset>0)and(odd(selector)))or@|
> ((file_offset>0)and(selector>=log_only)) then print_ln;
1313c1497
< a backslash.
---
> the user's escape character (which is usually a backslash).
1317c1501,1504
< begin print_char("\"); print(s);
---
> var c:integer; {the escape character code}
> begin @<Set variable |c| to the current escape character@>;
> if c>=0 then if c<256 then print(c);
> slow_print(s);
1320c1507
< @ An array of digits is printed by |print_digs|.
---
> @ An array of digits in the range |0..15| is printed by |print_the_digs|.
1323c1510,1511
< procedure print_digs(@!k:eight_bits); {prints |dig[k-1]|$\ldotsm$|dig[0]|}
---
> procedure print_the_digs(@!k:eight_bits);
> {prints |dig[k-1]|$\,\ldots\,$|dig[0]|}
1325,1326c1513,1516
< begin decr(k); print_char("0"+dig[k]);
< end;
---
> begin decr(k);
> if dig[k]<10 then print_char("0"+dig[k])
> else print_char("A"-10+dig[k]);
> end;
1337,1339c1527,1529
< var k:0..20; {index to current digit; we assume that $|n|<10^{20}$}
< @!m:nonnegative_integer; {used to negate |n| in possibly dangerous cases}
< begin k←0;
---
> var k:0..23; {index to current digit; we assume that $|n|<10^{23}$}
> @!m:integer; {used to negate |n| in possibly dangerous cases}
> begin k:=0;
1341,1349c1531,1539
< begin print_char("-");
< if n>-100000000 then n←-n
< else begin m←-1-n; n←m div 10; m←(m mod 10)+1; k←1;
< if m<10 then dig[0]←m
< else begin dig[0]←0; n←n+1;
< end;
< end;
< end;
< repeat dig[k]←n mod 10; n←n div 10; incr(k);
---
> begin print_char("-");
> if n>-100000000 then negate(n)
> else begin m:=-1-n; n:=m div 10; m:=(m mod 10)+1; k:=1;
> if m<10 then dig[0]:=m
> else begin dig[0]:=0; incr(n);
> end;
> end;
> end;
> repeat dig[k]:=n mod 10; n:=n div 10; incr(k);
1351c1541
< print_digs(k);
---
> print_the_digs(k);
1354c1544,1545
< @ Octal printing of nonnegative integers is accomplished by |print_octal|.
---
> @ Here is a trivial procedure to print two digits; it is usually called with
> a parameter in the range |0<=n<=99|.
1356,1361c1547,1549
< @p procedure print_octal(@!n:integer); {prints a positive integer in octal form}
< var k:0..22; {index to current digit; we assume that $0\L n<8^{22}$}
< begin k←0; print_char("'");
< repeat dig[k]←n mod 8; n←n div 8; incr(k);
< until n=0;
< print_digs(k);
---
> @p procedure print_two(@!n:integer); {prints two least significant digits}
> begin n:=abs(n) mod 100; print_char("0"+(n div 10));
> print_char("0"+(n mod 10));
1364,1365c1552
< @ In certain situations, \TeX\ prints either a standard visible ascii
< character or its octal ascii code.
---
> @ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|.
1367,1371c1554,1560
< @p procedure print_ascii(@!c:integer); {prints a character or its code}
< begin if (c>" ") and (c≤"~") then print_char(c)
< else begin if c<0 then print_char("-");
< print_octal(abs(c)); print_char(" ");
< end;
---
> @p procedure print_hex(@!n:integer);
> {prints a positive integer in hexadecimal form}
> var k:0..22; {index to current digit; we assume that $0\L n<16^{22}$}
> begin k:=0; print_char("""");
> repeat dig[k]:=n mod 16; n:=n div 16; incr(k);
> until n=0;
> print_the_digs(k);
1374,1377c1563,1572
< @ Roman numerals are produced by the |print_roman_int| routine, whose
< argument should be a positive integer. Readers who like puzzles might enjoy
< trying to figure out how this tricky code works; therefore no explanation
< will be given.
---
> @ Old versions of \TeX\ needed a procedure called |print_ASCII| whose function
> is now subsumed by |print|. We retain the old name here as a possible aid to
> future software arch\ae ologists.
>
> @d print_ASCII == print
>
> @ Roman numerals are produced by the |print_roman_int| routine. Readers
> who like puzzles might enjoy trying to figure out how this tricky code
> works; therefore no explanation will be given. Notice that 1990 yields
> \.{mcmxc}, not \.{mxm}.
1379c1574
< @p procedure print_roman_int(@!n:nonnegative_integer);
---
> @p procedure print_roman_int(@!n:integer);
1383,1397c1578,1592
< begin j←str_start["m2d5c2l5x2v5i"]; v←1000;
< loop@+ begin while n≥v do
< begin print_char(str_pool[j]); n←n-v;
< end;
< if n=0 then return;
< k←j+2; u←v div (str_pool[k-1]-"0");
< if str_pool[k-1]="2" then
< begin k←k+2; u←u div (str_pool[k-1]-"0");
< end;
< if n+u≥v then
< begin print_char(str_pool[k]); n←n+u;
< end
< else begin j←j+2; v←v div (str_pool[j-1]-"0");
< end;
< end;
---
> begin j:=str_start["m2d5c2l5x2v5i"]; v:=1000;
> loop@+ begin while n>=v do
> begin print_char(so(str_pool[j])); n:=n-v;
> end;
> if n<=0 then return; {nonpositive input produces no output}
> k:=j+2; u:=v div (so(str_pool[k-1])-"0");
> if str_pool[k-1]=si("2") then
> begin k:=k+2; u:=u div (so(str_pool[k-1])-"0");
> end;
> if n+u>=v then
> begin print_char(so(str_pool[k])); n:=n+u;
> end
> else begin j:=j+2; v:=v div (so(str_pool[j-1])-"0");
> end;
> end;
1405c1600
< begin j←str_start[str_ptr];
---
> begin j:=str_start[str_ptr];
1407,1408c1602,1603
< begin print_char(str_pool[j]); incr(j);
< end;
---
> begin print_char(so(str_pool[j])); incr(j);
> end;
1412c1607
< assuming that the |selector| setting is either |term_only| or |term_and_err|.
---
> assuming that the |selector| setting is either |term_only| or |term_and_log|.
1418,1419c1613,1614
< @d prompt_input(#)==begin print(#); term_input;
< end {prints a string and gets a line of input}
---
> @d prompt_input(#)==begin wake_up_terminal; print(#); term_input;
> end {prints a string and gets a line of input}
1423,1425c1618,1621
< begin update_terminal; {Now the user sees the prompt for sure}
< if not input_ln(term_in) then fatal_error("! End of file on the terminal");
< @.End of file on the terminal...@>
---
> begin update_terminal; {now the user sees the prompt for sure}
> if not input_ln(term_in,true) then fatal_error("End of file on the terminal!");
> @.End of file on the terminal@>
> term_offset:=0; {the user's line ended with \<\rm return>}
1427,1428c1623,1624
< if last≠first then for k←first to last-1 do print(buffer[k]);
< print_ln; incr(selector); {restore previous status, with |offset=0|}
---
> if last<>first then for k:=first to last-1 do print(buffer[k]);
> print_ln; incr(selector); {restore previous status}
1429a1626
>
1433c1630
< |print_nl("! Something anomalous has been detected");|\cr
---
> |print_err("Something anomalous has been detected");|\cr
1440c1637
< official error message that was printed. (Outside of the U.S.A., the help
---
> official error message that was printed. (Outside the U.S.A., the help
1444a1642,1643
> The |print_err| procedure supplies a `\.!' before the official message,
> and makes sure that the terminal is awake if a stop is going to occur.
1457a1657,1659
> @d print_err(#)==begin if interaction=error_stop_mode then wake_up_terminal;
> print_nl("! "); print(#);
> end
1462c1664
< @ @<Set init...@>=interaction←error_stop_mode;
---
> @ @<Set init...@>=interaction:=error_stop_mode;
1469c1671
< and |err_file| not yet open);
---
> and |log_file| not yet open);
1471c1673
< \hang|term_only| (when |interaction>batch_mode| and |err_file| not yet open);
---
> \hang|term_only| (when |interaction>batch_mode| and |log_file| not yet open);
1473c1675
< \hang|err_only| (when |interaction=batch_mode| and |err_file| is open);
---
> \hang|log_only| (when |interaction=batch_mode| and |log_file| is open);
1475c1677
< \hang|term_and_err| (when |interaction>batch_mode| and |err_file| is open).
---
> \hang|term_and_log| (when |interaction>batch_mode| and |log_file| is open).
1478c1680
< if interaction=batch_mode then selector←no_print@+else selector←term_only
---
> if interaction=batch_mode then selector:=no_print@+else selector:=term_only
1482a1685
> A similar interlock is provided by |set_box_allowed|.
1485,1486c1688,1690
< The global variable |spotless| is set to false when the first error
< has been detected.
---
> The global variable |history| records the worst level of error that
> has been detected. It has four possible values: |spotless|, |warning_issued|,
> |error_message_issued|, and |fatal_error_stop|.
1492a1697,1701
> @d spotless=0 {|history| value when nothing has been amiss yet}
> @d warning_issued=1 {|history| value when |begin_diagnostic| has been called}
> @d error_message_issued=2 {|history| value when |error| has been called}
> @d fatal_error_stop=3 {|history| value when termination was premature}
>
1495c1704,1705
< @!spotless:boolean; {has the source input been clean so far?}
---
> @!set_box_allowed:boolean; {is it safe to do a \.{\\setbox} assignment?}
> @!history:spotless..fatal_error_stop; {has the source input been clean so far?}
1497c1707
< last paragraph ended}
---
> last paragraph ended}
1499,1500c1709,1714
< @ @<Set init...@>=
< deletions_allowed←true; spotless←true; error_count←0;
---
> @ The value of |history| is initially |fatal_error_stop|, but it will
> be changed to |spotless| if \TeX\ survives the initialization process.
>
> @<Set init...@>=
> deletions_allowed:=true; set_box_allowed:=true;
> error_count:=0; {|history| is initialized elsewhere}
1508c1722
< when |get_token| is being used to delete a token, or if some fatal error
---
> when |get_token| is being used to delete a token, and/or if some fatal error
1511c1725
< is never more than one level deep.
---
> is never more than two levels deep.
1513a1728
> procedure@?normalize_selector; forward;@t\2@>@/
1517a1733
> procedure@?open_log_file; forward;@t\2@>@/
1520c1736,1738
< @t\4@>@;@+debug@+procedure@?debug_help; forward;@;@+gubed
---
> procedure@?give_err_help; forward;@t\2@>@/
> @t\4\hskip-\fontdimen2\font@>@;@+@!debug@+procedure@?debug_help;
> forward;@;@+gubed
1524c1742
< in reverse order, i.e., with |help_line[0]| last.
---
> in reverse order, i.e., with |help_line[0]| appearing last.
1526,1538c1744,1756
< @d hlp1(#)==help_line[0]←#;@+end
< @d hlp2(#)==help_line[1]←#; hlp1
< @d hlp3(#)==help_line[2]←#; hlp2
< @d hlp4(#)==help_line[3]←#; hlp3
< @d hlp5(#)==help_line[4]←#; hlp4
< @d hlp6(#)==help_line[5]←#; hlp5
< @d help0==help_ptr←0 {sometimes there might be no help}
< @d help1==@+begin help_ptr←1; hlp1 {use this with one help line}
< @d help2==@+begin help_ptr←2; hlp2 {use this with two help lines}
< @d help3==@+begin help_ptr←3; hlp3 {use this with three help lines}
< @d help4==@+begin help_ptr←4; hlp4 {use this with four help lines}
< @d help5==@+begin help_ptr←5; hlp5 {use this with five help lines}
< @d help6==@+begin help_ptr←6; hlp6 {use this with six help lines}
---
> @d hlp1(#)==help_line[0]:=#;@+end
> @d hlp2(#)==help_line[1]:=#; hlp1
> @d hlp3(#)==help_line[2]:=#; hlp2
> @d hlp4(#)==help_line[3]:=#; hlp3
> @d hlp5(#)==help_line[4]:=#; hlp4
> @d hlp6(#)==help_line[5]:=#; hlp5
> @d help0==help_ptr:=0 {sometimes there might be no help}
> @d help1==@+begin help_ptr:=1; hlp1 {use this with one help line}
> @d help2==@+begin help_ptr:=2; hlp2 {use this with two help lines}
> @d help3==@+begin help_ptr:=3; hlp3 {use this with three help lines}
> @d help4==@+begin help_ptr:=4; hlp4 {use this with four help lines}
> @d help5==@+begin help_ptr:=5; hlp5 {use this with five help lines}
> @d help6==@+begin help_ptr:=6; hlp6 {use this with six help lines}
1542a1761,1764
> @!use_err_help:boolean; {should the |err_help| list be shown?}
>
> @ @<Set init...@>=
> help_ptr:=0; use_err_help:=false;
1544,1545c1766,1767
< @ The |quit| procedure just cuts across all active procedure levels and jumps
< out to |end_of_TEX|. This is the only nonlocal |@!goto| statement in the
---
> @ The |jump_out| procedure just cuts across all active procedure levels and
> goes to |end_of_TEX|. This is the only nontrivial |@!goto| statement in the
1550,1551c1772,1774
< In such cases the body of |quit| should simply be `|close_files_and_terminate|;'
< followed by a call on some system procedure that quietly terminates the program.
---
> In such cases the body of |jump_out| should simply be
> `|close_files_and_terminate|;\thinspace' followed by a call on some system
> procedure that quietly terminates the program.
1554c1777
< procedure quit;
---
> procedure jump_out;
1562,1565c1785,1790
< label continue, exit;
< var c:ascii_code; {what the user types}
< @!s1,@!s2,@!s3:integer; {used to save global variables when deleting tokens}
< begin print_char("."); show_context;
---
> label continue,exit;
> var c:ASCII_code; {what the user types}
> @!s1,@!s2,@!s3,@!s4:integer;
> {used to save global variables when deleting tokens}
> begin if history<error_message_issued then history:=error_message_issued;
> print_char("."); show_context;
1569c1794
< begin print_nl("(That makes 100 errors; please try again.)");
---
> begin print_nl("(That makes 100 errors; please try again.)");
1571,1572c1796,1797
< quit;
< end;
---
> history:=fatal_error_stop; jump_out;
> end;
1578,1582c1803,1808
< if last=first then return;
< c←buffer[first];
< if c≥"a" then c←c+"A"-"a"; {convert to upper case}
< @<Interpret code |c| and |return| if done@>;
< end
---
> @.?\relax@>
> if last=first then return;
> c:=buffer[first];
> if c>="a" then c:=c+"A"-"a"; {convert to uppercase}
> @<Interpret code |c| and |return| if done@>;
> end
1587,1591c1813,1815
< wizardry, so it is not standard in \TeX\ and not included here except
< as a recommendation. (The name of the file to edit, if an `\.E' option is
< added, is |input_stack[base_ptr].name_field|, provided that |base_ptr>0|;
< the value of |base_ptr| has been set by |show_context|.
< No input files are open when |base_ptr=0|.)
---
> wizardry, so the present implementation simply types out the name of the
> file that should be
> edited and the relevant line number.
1594,1595c1818,1819
< There is a secret `\.D' option available when the debugging routines have
< not been commented out.
---
> There is a secret `\.D' option available when the debugging routines haven't
> been commented~out.
1600,1603c1824,1834
< "1","2","3","4","5","6","7","8","9": if deletions_allowed then
< @<Delete |c-"0"| tokens, |goto continue|@>;
< @t\4\4@>@;@+debug "D": begin debug_help; goto continue;@+end;@+gubed@/
< "H": @<Print the help information, |goto continue|@>;
---
> "0","1","2","3","4","5","6","7","8","9": if deletions_allowed then
> @<Delete \(c)|c-"0"| tokens and |goto continue|@>;
> @t\4\4@>@;@+@!debug "D": begin debug_help; goto continue;@+end;@+gubed@/
> "E": if base_ptr>0 then
> begin print_nl("You want to edit file ");
> @.You want to edit file x@>
> slow_print(input_stack[base_ptr].name_field);
> print(" at line "); print_int(line);
> interaction:=scroll_mode; jump_out;
> end;
> "H": @<Print the help information and |goto continue|@>;
1606,1610c1837,1838
< "X":begin prompt_input("Type X again to exit:");
< if (last>first)∧((buffer[first]="x")∨(buffer[first]="X")) then
< begin interaction←scroll_mode; quit;
< end;
< end;
---
> "X":begin interaction:=scroll_mode; jump_out;
> end;
1615,1620c1843,1845
< @ The `\.E' option in the following menu
< should be taken out of braces if it is implemented.
< @^system dependencies@>
<
< @<Print the menu...@>=
< print("Type <return> to proceed, S to scroll future error messages,");@/
---
> @ @<Print the menu...@>=
> begin print("Type <return> to proceed, S to scroll future error messages,");@/
> @.Type <return> to proceed...@>
1622,1623c1847,1848
< print_nl("I to insert something, ");@/
< @{@,@,@+if base_ptr>0 then print("E to edit your file,");@;@+@}@/
---
> print_nl("I to insert something, ");
> if base_ptr>0 then print("E to edit your file,");
1625,1626c1850,1852
< print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
< print_nl("H for help, X to quit.")
---
> print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
> print_nl("H for help, X to quit.");
> end
1634,1636c1860,1868
< begin error_count←0; interaction←batch_mode+c-"Q";
< if c="Q" then decr(selector);
< print_ln; return;
---
> begin error_count:=0; interaction:=batch_mode+c-"Q";
> print("OK, entering ");
> case c of
> "Q":begin print_esc("batchmode"); decr(selector);
> end;
> "R":print_esc("nonstopmode");
> "S":print_esc("scrollmode");
> end; {there are no other cases}
> print("..."); print_ln; update_terminal; return;
1639,1642c1871,1874
< @ When the following code is executed, the material inserted by the user
< may appear in |buffer[(first+1)..(last-1)]|; otherwise another prompt
< will be given. In order to fully understand this part of the program,
< you need to be familiar with \TeX's input stacks.
---
> @ When the following code is executed, |buffer[(first+1)..(last-1)]| may
> contain the material inserted by the user; otherwise another prompt will
> be given. In order to understand this part of the program fully, you need
> to be familiar with \TeX's input stacks.
1646c1878
< {now |state←mid_line|, so an initial blank space will count as a blank}
---
> {now |state=mid_line|, so an initial blank space will count as a blank}
1648,1653c1880,1886
< begin loc←first+1; buffer[first]←" ";
< end
< else begin prompt_input("**"); loc←first;
< end;
< first←last;
< cur_input.limit_field←last-1; {no |carriage_return| ends this line}
---
> begin loc:=first+1; buffer[first]:=" ";
> end
> else begin prompt_input("insert>"); loc:=first;
> @.insert>@>
> end;
> first:=last;
> cur_input.limit_field:=last-1; {no |end_line_char| ends this line}
1659,1663c1892,1897
< @<Delete |c-"0"| tokens...@>=
< begin s1←cur_tok; s2←cur_cmd; s3←cur_chr; OK_to_interrupt←false;
< if (last>first+1) and (buffer[first+1]≥"0")∧(buffer[first+1]≤"9") then
< c←c*10+buffer[first+1]-"0"*11
< else c←c-"0";
---
> @<Delete \(c)|c-"0"| tokens...@>=
> begin s1:=cur_tok; s2:=cur_cmd; s3:=cur_chr; s4:=align_state;
> align_state:=1000000; OK_to_interrupt:=false;
> if (last>first+1) and (buffer[first+1]>="0")and(buffer[first+1]<="9") then
> c:=c*10+buffer[first+1]-"0"*11
> else c:=c-"0";
1665,1668c1899,1902
< begin get_token; {one-level recursive call of |error| is possible}
< decr(c);
< end;
< cur_tok←s1; cur_cmd←s2; cur_chr←s3; OK_to_interrupt←true;
---
> begin get_token; {one-level recursive call of |error| is possible}
> decr(c);
> end;
> cur_tok:=s1; cur_cmd:=s2; cur_chr:=s3; align_state:=s4; OK_to_interrupt:=true;
1675,1679c1909,1917
< begin if help_ptr=0 then
< help2("Sorry, I don't know how to help in this situation.")@/
< ("Maybe you should try asking a human?");
< repeat decr(help_ptr); print(help_line[help_ptr]); print_ln;
< until help_ptr=0;
---
> begin if use_err_help then
> begin give_err_help; use_err_help:=false;
> end
> else begin if help_ptr=0 then
> help2("Sorry, I don't know how to help in this situation.")@/
> @t\kern1em@>("Maybe you should try asking a human?");
> repeat decr(help_ptr); print(help_line[help_ptr]); print_ln;
> until help_ptr=0;
> end;
1681,1683c1919,1921
< ("Maybe you should try asking a human?")@/
< ("An error might have occurred before I noticed any problems.")@/
< ("``If all else fails, read the instructions.''");
---
> ("Maybe you should try asking a human?")@/
> ("An error might have occurred before I noticed any problems.")@/
> ("``If all else fails, read the instructions.''");@/
1689,1691c1927,1932
< while help_ptr>0 do
< begin decr(help_ptr); print_nl(help_line[help_ptr]);
< end;
---
> if use_err_help then
> begin print_ln; give_err_help;
> end
> else while help_ptr>0 do
> begin decr(help_ptr); print_nl(help_line[help_ptr]);
> end;
1693c1934
< if interaction>batch_mode then incr(selector); {enable terminal output}
---
> if interaction>batch_mode then incr(selector); {re-enable terminal output}
1702a1944,1954
> @ In anomalous cases, the print selector might be in an unknown state;
> the following subroutine is called to fix things just enough to keep
> running a bit longer.
>
> @p procedure normalize_selector;
> begin if log_opened then selector:=term_and_log
> else selector:=term_only;
> if job_name=0 then open_log_file;
> if interaction=batch_mode then decr(selector);
> end;
>
1706,1708c1958,1962
< interaction←scroll_mode; {no more interaction}
< error; quit; {irrecoverable error}
< end
---
> interaction:=scroll_mode; {no more interaction}
> if log_opened then error;
> @!debug if interaction>batch_mode then debug_help;@+gubed@;@/
> history:=fatal_error_stop; jump_out; {irrecoverable error}
> end
1712c1966,1967
< begin print_nl("! Emergency stop"); help1(s); succumb;
---
> begin normalize_selector;@/
> print_err("Emergency stop"); help1(s); succumb;
1720,1722c1975,1978
< begin print_nl("! TeX capacity exceeded, sorry [");
< @.TeX capacity exceeded...@>
< print(s); print_char("="); print_int(n); print_char("]");
---
> begin normalize_selector;
> print_err("TeX capacity exceeded, sorry [");
> @.TeX capacity exceeded ...@>
> print(s); print_char("="); print_int(n); print_char("]");
1724c1980
< ("you can ask a wizard to enlarge me.");
---
> ("you can ask a wizard to enlarge me.");
1728,1730c1984,1989
< @ The program might sometime run completely amok and print out the
< following message, which is really intended for the \TeX\ maintenance
< person instead of the user.
---
> @ The program might sometime run completely amok, at which point there is
> no choice but to stop. If no previous error has been detected, that's bad
> news; a message is printed that is really intended for the \TeX\
> maintenance person instead of the user (unless the user has been
> particularly diabolical). The index entries for `this can't happen' may
> help to pinpoint the problem.
1735,1737c1994,1997
< {consistency check violated; |s| tells where}
< begin if spotless then
< begin print_nl("! This can't happen ("); print(s); print_char(")");
---
> {consistency check violated; |s| tells where}
> begin normalize_selector;
> if history<error_message_issued then
> begin print_err("This can't happen ("); print(s); print_char(")");
1739,1741c1999,2001
< help1("I'm broken. Please show this to someone who can fix can fix");
< end
< else begin print_nl("! I can't go on meeting you like this");
---
> help1("I'm broken. Please show this to someone who can fix can fix");
> end
> else begin print_err("I can't go on meeting you like this");
1743,1745c2003,2005
< help2("One of your earlier faux pas has wounded me deeply,")@/
< ("so I'm barely conscious. Please fix it and try again.");
< end;
---
> help2("One of your faux pas seems to have wounded me deeply...")@/
> ("in fact, I'm barely conscious. Please fix it and try again.");
> end;
1757,1758c2017,2018
< @d check_interrupt==begin if interrupt≠0 then pause_for_instructions;
< end
---
> @d check_interrupt==begin if interrupt<>0 then pause_for_instructions;
> end
1761c2021
< @!interrupt:integer; {should \TeX\ pause for instruction?}
---
> @!interrupt:integer; {should \TeX\ pause for instructions?}
1765c2025
< interrupt←0; OK_to_interrupt←true;
---
> interrupt:=0; OK_to_interrupt:=true;
1767,1768c2027,2028
< @ When an interrupt has been detected, the program comes goes into its
< highest interaction level and lets the user have the full flexibility of
---
> @ When an interrupt has been detected, the program goes into its
> highest interaction level and lets the user have nearly the full flexibility of
1774,1777c2034,2037
< begin interaction←error_stop_mode;
< if (selector=err_only)∨(selector=no_print) then
< incr(selector);
< print_nl("! Interruption");
---
> begin interaction:=error_stop_mode;
> if (selector=log_only)or(selector=no_print) then
> incr(selector);
> print_err("Interruption");
1779,1784c2039,2044
< help3("You rang?")@/
< ("Try to insert some instructions for me (e.g.,`I\showlists'),")@/
< ("unless you just want to quit by typing `X'.");
< deletions_allowed←false; error; deletions_allowed←true;
< interrupt←0;
< end;
---
> help3("You rang?")@/
> ("Try to insert some instructions for me (e.g.,`I\showlists'),")@/
> ("unless you just want to quit by typing `X'.");
> deletions_allowed:=false; error; deletions_allowed:=true;
> interrupt:=0;
> end;
1785a2046
>
1802c2063,2066
< numerator. These are harmless; see |div| in the index.)
---
> numerator. These are harmless; see |div| in the index. Also if the user
> sets the \.{\\time} or the \.{\\year} to a negative value, some diagnostic
> information will involve negative-numerator division. The same remarks
> apply for |mod| as well as for |div|.)
1808,1809c2072,2073
< begin if odd(x) then half←(x+1) div 2
< else half←x @!div 2;
---
> begin if odd(x) then half:=(x+1) div 2
> else half:=x @!div 2;
1824,1825c2088,2089
< @ The following function is used to create a scaled integer from a decimal
< fraction $(.d↓0d↓1\ldotsm d↓{k-1})$, where |0≤k≤16|. The digit $d↓i$ is
---
> @ The following function is used to create a scaled integer from a given decimal
> fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is
1829c2093
< {converts a decimal fraction}
---
> {converts a decimal fraction}
1831c2095
< begin a←0;
---
> begin a:=0;
1833,1835c2097,2099
< begin decr(k); a←(a+dig[k]*two) div 10;
< end;
< round_decimals←(a+1) div 2;
---
> begin decr(k); a:=(a+dig[k]*two) div 10;
> end;
> round_decimals:=(a+1) div 2;
1841,1844c2105,2112
< be reproduced exactly. [{\sl Proof:\/} If round$(x)=\lfloor
< x+{1\over2}\rfloor$ and if $\alpha<1$, it is not difficult to verify that
< round$(\alpha\,\hbox{round}( \alpha^{-1}n))=n$ for all integers $n$. In
< our case $\alpha=2^{16}/10^5$.]
---
> be reproduced exactly; the ``simplest'' such decimal number is output,
> but there is always at least one digit following the decimal point.
>
> The invariant relation in the \&{repeat} loop is that a sequence of
> decimal digits yet to be printed will yield the original number if and only if
> they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
> We can stop if and only if $f=0$ satisfies this condition; the loop will
> terminate before $s$ can possibly become zero.
1847,1848c2115,2116
< digits}
< var k:0..4; {index to current digit of the fraction part}
---
> digits}
> var delta:scaled; {amount of allowable inaccuracy}
1850,1851c2118,2119
< begin print_char("-"); s←-s; {print the sign, if negative}
< end;
---
> begin print_char("-"); negate(s); {print the sign, if negative}
> end;
1853,1854d2120
< s←((s mod unity) * 3125 + 1024) div 2048;
< {now |0≤s<100000| is the fraction part}
1856,1857c2122,2125
< repeat print_char("0"+(s div 10000)); s←10*(s mod 10000);
< until s=0;
---
> s:=10*(s mod unity)+5; delta:=10;
> repeat if delta>unity then s:=s+@'100000-50000; {round the last digit}
> print_char("0"+(s div unity)); s:=10*(s mod unity); delta:=delta*10;
> until s<=delta;
1871c2139
< @^Overflow in arithmetic@>
---
> @^overflow in arithmetic@>
1873c2141
< few dozen tests of the form `\!|if x≥@'10000000000 then
---
> few dozen tests of the form `\ignorespaces|if x>=@'10000000000 then
1890c2158,2159
< and@@|y| are |scaled| and |n| is an integer.
---
> and~|y| are |scaled| and |n| is an integer. We will also use it to
> multiply integers.
1892c2161,2164
< @p function nx_plus_y(@!n:integer;@!x,@!y:scaled):scaled;
---
> @d nx_plus_y(#)==mult_and_add(#,@'7777777777)
> @d mult_integers(#)==mult_and_add(#,0,@'17777777777)
>
> @p function mult_and_add(@!n:integer;@!x,@!y,@!max_answer:scaled):scaled;
1894,1900c2166,2172
< begin x←-x; n←-n;
< end;
< if n=0 then nx_plus_y←y
< else if ((x≤(@'7777777777-y) div n)∧(-x≤(@'7777777777+y) div n)) then
< nx_plus_y←n*x+y
< else begin arith_error←true; nx_plus_y←0;
< end;
---
> begin negate(x); negate(n);
> end;
> if n=0 then mult_and_add:=y
> else if ((x<=(max_answer-y) div n)and(-x<=(max_answer+y) div n)) then
> mult_and_add:=n*x+y
> else begin arith_error:=true; mult_and_add:=0;
> end;
1906,1907c2178,2179
< var negate:boolean; {should |remainder| be negated?}
< begin negate←false;
---
> var negative:boolean; {should |remainder| be negated?}
> begin negative:=false;
1909,1920c2181,2192
< begin arith_error←true; x_over_n←0; remainder←x;
< end
< else begin if n<0 then
< begin x←-x; n←-n; negate←true;
< end;
< if x≥0 then
< begin x_over_n←x div n; remainder←x mod n;
< end
< else begin x_over_n←-((-x) div n); remainder←-((-x) mod n);
< end;
< end;
< if negate then remainder←-remainder;
---
> begin arith_error:=true; x_over_n:=0; remainder:=x;
> end
> else begin if n<0 then
> begin negate(x); negate(n); negative:=true;
> end;
> if x>=0 then
> begin x_over_n:=x div n; remainder:=x mod n;
> end
> else begin x_over_n:=-((-x) div n); remainder:=-((-x) mod n);
> end;
> end;
> if negative then negate(remainder);
1924,1926c2196,2198
< where |n| and |d| are nonnegative integers |≤@t$2^{16}$@>| and |d| is
< positive. It would be too dangerous to multiply by@@|n| and then divide
< by@@|d|, in separate operations, since overflow might well occur; and it
---
> where |n| and |d| are nonnegative integers |<=@t$2^{16}$@>| and |d| is
> positive. It would be too dangerous to multiply by~|n| and then divide
> by~|d|, in separate operations, since overflow might well occur; and it
1931c2203
< var positive:boolean; {was |x≥0|?}
---
> var positive:boolean; {was |x>=0|?}
1933,1940c2205,2212
< begin if x≥0 then positive←true
< else begin x←-x; positive←false;
< end;
< t←(x mod @'100000)*n;
< u←(x div @'100000)*n+(t div @'100000);
< v←(u mod d)*@'100000 + (t mod @'100000);
< if u div d≥@'100000 then arith_error←true
< else u←@'100000*(u div d) + (v div d);
---
> begin if x>=0 then positive:=true
> else begin negate(x); positive:=false;
> end;
> t:=(x mod @'100000)*n;
> u:=(x div @'100000)*n+(t div @'100000);
> v:=(u mod d)*@'100000 + (t mod @'100000);
> if u div d>=@'100000 then arith_error:=true
> else u:=@'100000*(u div d) + (v div d);
1942,1945c2214,2217
< begin xn_over_d←u; remainder←v mod d;
< end
< else begin xn_over_d←-u; remainder←-(v mod d);
< end;
---
> begin xn_over_d:=u; remainder:=v mod d;
> end
> else begin xn_over_d:=-u; remainder:=-(v mod d);
> end;
1949,1950c2221,2222
< total@@|t| is supposed to be made from amounts that sum to@@|s|. According
< to the \TeX\ manual, the badness of this situation is $100(t/s)^3$;
---
> total~|t| is supposed to be made from amounts that sum to~|s|. According
> to {\sl The \TeX book}, the badness of this situation is $100(t/s)^3$;
1953a2226
> @:TeXbook}{\sl The \TeX book@>
1961,1962c2234,2235
< It is not difficult to prove that $$\hbox{|badness(t+1,s)≥badness(t,s)
< ≥badness(t,s+1)|}$$. The badness function defined here is capable of
---
> It is not difficult to prove that $$\hbox{|badness(t+1,s)>=badness(t,s)
> >=badness(t,s+1)|}.$$ The badness function defined here is capable of
1967c2240
< @p function badness(@!t,@!s:scaled):halfword; {compute badness, given |t≥0|}
---
> @p function badness(@!t,@!s:scaled):halfword; {compute badness, given |t>=0|}
1969,1977c2242,2250
< 100\cdot2^{18}$}
< begin if t=0 then badness←0
< else if s≤0 then badness←inf_bad
< else begin if t≤7230584 then r←(t*297) div s {$297^3=99.94\times2^{18}$}
< else if s≥1663497 then r←t div (s div 297)
< else r←t;
< if r>1290 then badness←inf_bad {$1290^3<2^{31}<1291^3$}
< else badness←(r*r*r+@'400000) div @'1000000;
< end; {that was $r^3/2^{18}$, rounded to the nearest integer}
---
> 100\cdot2^{18}$}
> begin if t=0 then badness:=0
> else if s<=0 then badness:=inf_bad
> else begin if t<=7230584 then r:=(t*297) div s {$297^3=99.94\times2^{18}$}
> else if s>=1663497 then r:=t div (s div 297)
> else r:=t;
> if r>1290 then badness:=inf_bad {$1290^3<2^{31}<1291^3$}
> else badness:=(r*r*r+@'400000) div @'1000000;
> end; {that was $r^3/2^{18}$, rounded to the nearest integer}
1987c2260
< An unsigned glue ratio should take the same amount of memory as an
---
> A glue ratio should take the same amount of memory as an
1990c2263
< |short_real| in some implementations of \PASCAL. Alter\-natively,
---
> |short_real| in some implementations of \PASCAL. Alternatively,
1992c2265,2266
< arithmetic; see {\sl TUGboat \bf3} (1982), 10--27.
---
> arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the
> routines cited there must be modified to allow negative glue ratios.)
1994a2269,2274
> @d set_glue_ratio_zero(#) == #:=0.0 {store the representation of zero ratio}
> @d set_glue_ratio_one(#) == #:=1.0 {store the representation of unit ratio}
> @d float(#) == # {convert from |glue_ratio| to type |real|}
> @d unfloat(#) == # {convert from |real| to type |glue_ratio|}
> @d float_constant(#) == #.0 {convert |integer| constant to |real|}
>
1996a2277
>
2000c2281
< possibly scaled, or an (unsigned) |glue_ratio|, or a small number of
---
> possibly scaled, or a (signed) |glue_ratio|, or a small number of
2012c2293
< field)\cr
---
> field)\cr
2014c2295
< &\qquad\qquad\qquad(four quarterword fields)\cr}}$$
---
> &\qquad\qquad\qquad(four quarterword fields)\cr}}$$
2039c2320
< so it makes rather general assumptions. If integers having the subrange
---
> so it makes few assumptions. If integers having the subrange
2057,2064c2338,2350
< if (min_quarterword>0)∨(max_quarterword<127) then bad←11;
< if (min_halfword>0)∨(max_halfword<32767) then bad←12;
< if (min_quarterword<min_halfword)∨@|
< (max_quarterword>max_halfword) then bad←13;
< if (mem_base<min_halfword)∨(mem_max≥max_halfword) then bad←14;
< if (font_base<min_quarterword)∨(font_max>max_quarterword) then bad←15;
< if (save_size>max_halfword)∨(max_strings>max_halfword) then bad←16;
< if (buf_size>max_halfword) then bad←17;
---
> init if (mem_min<>mem_bot)or(mem_max<>mem_top) then bad:=10;@+tini@;@/
> if (mem_min>mem_bot)or(mem_max<mem_top) then bad:=10;
> if (min_quarterword>0)or(max_quarterword<127) then bad:=11;
> if (min_halfword>0)or(max_halfword<32767) then bad:=12;
> if (min_quarterword<min_halfword)or@|
> (max_quarterword>max_halfword) then bad:=13;
> if (mem_min<min_halfword)or(mem_max>=max_halfword)or@|
> (mem_bot-mem_min>max_halfword+1) then bad:=14;
> if (font_base<min_quarterword)or(font_max>max_quarterword) then bad:=15;
> if font_max>font_base+256 then bad:=16;
> if (save_size>max_halfword)or(max_strings>max_halfword) then bad:=17;
> if buf_size>max_halfword then bad:=18;
> if max_quarterword-min_quarterword<255 then bad:=19;
2074c2360
< @^inner loop@>
---
> @^inner loop@>@^system dependencies@>
2076,2079c2362,2369
< @d qi(#)==(#+min_quarterword)
< {to put an |eight_bits| item into a quarterword}
< @d qo(#)==(#-min_quarterword)
< {to take an |eight_bits| item out of a quarterword}
---
> @d qi(#)==#+min_quarterword
> {to put an |eight_bits| item into a quarterword}
> @d qo(#)==#-min_quarterword
> {to take an |eight_bits| item out of a quarterword}
> @d hi(#)==#+min_halfword
> {to put a sixteen-bit item into a halfword}
> @d ho(#)==#-min_halfword
> {to take a sixteen-bit item from a halfword}
2092,2096c2382,2386
< @!rh:halfword;
< case two_choices of
< 1: (@!lh:halfword);
< 2: (@!b0:quarterword; @!b1:quarterword);
< end;
---
> @!rh:halfword;
> case two_choices of
> 1: (@!lh:halfword);
> 2: (@!b0:quarterword; @!b1:quarterword);
> end;
2098,2109c2388,2399
< @!b0:quarterword;
< @!b1:quarterword;
< @!b2:quarterword;
< @!b3:quarterword;
< end;
< @!memory_word = packed record@;@/
< case four_choices of
< 1: (@!int:integer);
< 2: (@!gr:glue_ratio);
< 3: (@!hh:two_halves);
< 4: (@!qqqq:four_quarters);
< end;
---
> @!b0:quarterword;
> @!b1:quarterword;
> @!b2:quarterword;
> @!b3:quarterword;
> end;
> @!memory_word = record@;@/
> case four_choices of
> 1: (@!int:integer);
> 2: (@!gr:glue_ratio);
> 3: (@!hh:two_halves);
> 4: (@!qqqq:four_quarters);
> end;
2116c2406,2407
< @p debug procedure print_memory_word(@!w:memory_word); {prints |w| in all ways}
---
> @p @!debug procedure print_word(@!w:memory_word);
> {prints |w| in all ways}
2119c2410,2411
< print_scaled(round(unity*w.gr)); print_ln;@/
---
> print_scaled(round(unity*float(w.gr))); print_ln;@/
> @^real multiplication@>
2125a2418
>
2127c2420
< The \TeX\ system does nearly all of its own mem\-ory allocation, so that it
---
> The \TeX\ system does nearly all of its own memory allocation, so that it
2135c2428
< called |eqtb| that will be explained below. A pointer variable might
---
> called |eqtb| that will be explained later. A pointer variable might
2138c2431
< value represents a null pointer.
---
> value represents a null pointer. \TeX\ does not assume that |mem[null]| exists.
2146,2147c2439,2442
< @ The |mem| array is divided once and for all into two regions that are
< allocated separately. Locations less than |hi_mem_base| are used for storing
---
> @ The |mem| array is divided into two regions that are allocated separately,
> but the dividing line between these two regions is not fixed; they grow
> together until finding their ``natural'' size in a particular job.
> Locations less than or equal to |lo_mem_max| are used for storing
2151,2162c2446,2466
< appears in the allocated nodes: the program is responsible for knowing the
< relevant size when a node is freed. The remaining region of |mem| is
< allocated in single words using a conventional \.{AVAIL} stack.
<
< Incidentally, it would be feasible to construct implementations of \TeX\ that
< are based on 16-bit words instead of 32-bit words, for machines having
< comparatively small memories. In such cases it would be desirable to have
< two parallel arrays for the upper part of memory, called say \\{mem\_link}|[p]|
< and \\{mem\_info}|[p]|,
< since the single-word region in the present implementation
< consists entirely of |memory_word| items of type |two_halves|.
< @^small computers@>
---
> appears in the allocated nodes; the program is responsible for knowing the
> relevant size when a node is freed. Locations greater than or equal to
> |hi_mem_min| are used for storing one-word records; a conventional
> \.{AVAIL} stack is used for allocation in this region.
>
> Locations of |mem| between |mem_bot| and |mem_top| may be dumped as part
> of preloaded format files, by the \.{INITEX} preprocessor.
> @.INITEX@>
> Production versions of \TeX\ may extend the memory at both ends in order to
> provide more space; locations between |mem_min| and |mem_bot| are always
> used for variable-size nodes, and locations between |mem_top| and |mem_max|
> are always used for single-word nodes.
>
> The key pointers that govern |mem| allocation have a prescribed order:
> $$\advance\thickmuskip-2mu
> \hbox{|null<=mem_min<=mem_bot<lo_mem_max<
> hi_mem_min<mem_top<=mem_end<=mem_max|.}$$
>
> Empirical tests show that the present implementation of \TeX\ tends to
> spend about 9\pct! of its running time allocating nodes, and about 6\pct!
> deallocating them after their use.
2164,2165c2468,2471
< @<Globals...@>=
< @!mem : array[mem_base..mem_max] of memory_word; {the big dynamic storage area}
---
> @<Glob...@>=
> @!mem : array[mem_min..mem_max] of memory_word; {the big dynamic storage area}
> @!lo_mem_max : pointer; {the largest location of variable-size memory in use}
> @!hi_mem_min : pointer; {the smallest location of one-word memory in use}
2169c2475
< maximum memory usage. When code between the delimiters |stat| $\ldots$
---
> maximum memory usage. When code between the delimiters |@!stat| $\ldots$
2171,2172c2477
< report these statistics whenever a page is output and |tracing_stats| is
< nonzero.
---
> report these statistics when |tracing_stats| is sufficiently large.
2174c2479
< @<Globals...@>=
---
> @<Glob...@>=
2176d2480
< @!max_var_used : integer; {how much memory was in use}
2181c2485
< occur between |hi_mem_base| and |mem_end|, inclusive, are of type
---
> occur between |hi_mem_min| and |mem_end|, inclusive, are of type
2191c2495
< @<Globals...@>=
---
> @<Glob...@>=
2195c2499
< @ If one-word memory is exhausted, it might mean that the user has forgotten
---
> @ If memory is exhausted, it might mean that the user has forgotten
2203a2508,2513
> @^inner loop@>
>
> If the available-space list is empty, i.e., if |avail=null|,
> we try first to increase |mem_end|. If that cannot be done, i.e., if
> |mem_end=mem_max|, we try to decrease |hi_mem_min|. If that cannot be
> done, i.e., if |hi_mem_min=lo_mem_max+1|, we have to quit.
2207,2208c2517,2518
< begin p←avail; {get top location in the |avail| stack}
< if p≠null then avail←link(avail) {and pop it off}
---
> begin p:=avail; {get top location in the |avail| stack}
> if p<>null then avail:=link(avail) {and pop it off}
2210,2217c2520,2532
< begin incr(mem_end); p←mem_end;
< end
< else begin runaway; {if memory is exhausted, display possible runaway text}
< overflow("memory size",mem_max-mem_base); {quit; all one-word nodes are busy}
< end;
< link(p)←null; {provide an oft-desired initialization of the new node}
< stat incr(dyn_used);@+tats@;{maintain statistics}
< get_avail←p;
---
> begin incr(mem_end); p:=mem_end;
> end
> else begin decr(hi_mem_min); p:=hi_mem_min;
> if hi_mem_min<=lo_mem_max then
> begin runaway; {if memory is exhausted, display possible runaway text}
> overflow("main memory size",mem_max+1-mem_min);
> {quit; all one-word nodes are busy}
> @:TeX capacity exceeded main memory size}{\quad main memory size@>
> end;
> end;
> link(p):=null; {provide an oft-desired initialization of the new node}
> @!stat incr(dyn_used);@+tats@;{maintain statistics}
> get_avail:=p;
2225,2227c2540,2555
< begin link(#)←avail; avail←#;
< stat decr(dyn_used);@+tats@/
< end
---
> begin link(#):=avail; avail:=#;
> @!stat decr(dyn_used);@+tats@/
> end
>
> @ There's also a |fast_get_avail| routine, which saves the procedure-call
> overhead at the expense of extra programming. This routine is used in
> the places that would otherwise account for the most calls of |get_avail|.
> @^inner loop@>
>
> @d fast_get_avail(#)==@t@>@;@/
> begin #:=avail; {avoid |get_avail| if possible, to save time}
> if #=null then #:=get_avail
> else begin avail:=link(#); link(#):=null;
> @!stat incr(dyn_used);@+tats@/
> end;
> end
2230a2559
> @^inner loop@>
2233,2237c2562,2569
< available}
< var q:pointer; {the successor of node |p|}
< begin while p≠null do
< begin q←link(p); free_avail(p); p←q;
< end;
---
> available}
> var @!q,@!r:pointer; {list traversers}
> begin if p<>null then
> begin r:=p;
> repeat q:=r; r:=link(r); @!stat decr(dyn_used);@+tats@/
> until r=null; {now |q| is the last node on the list}
> link(q):=avail; avail:=p;
> end;
2252a2585,2587
> (We require |mem_max<max_halfword| because terrible things can happen
> when |max_halfword| appears in the |link| field of a nonempty node.)
>
2259c2594
< @<Globals...@>=
---
> @<Glob...@>=
2263c2598
< of size@@|s|, which must be 2@@or more. The |link| field of the first word
---
> of size~|s|, which must be 2~or more. The |link| field of the first word
2270,2271c2605,2606
< @p function get_node(@!s:integer):pointer; {variable-size node liberation}
< label found,exit;
---
> @p function get_node(@!s:integer):pointer; {variable-size node allocation}
> label found,exit,restart;
2275c2610,2611
< begin p←rover; {start at some free node in the ring}
---
> @!t:integer; {temporary register}
> begin restart: p:=rover; {start at some free node in the ring}
2277,2278c2613,2615
< and |goto found| if allocation was possible@>;
< p←rlink(p); {move to the next node in the ring}
---
> and |goto found| if allocation was possible@>;
> @^inner loop@>
> p:=rlink(p); {move to the next node in the ring}
2281,2286c2618,2626
< begin get_node←max_halfword; return;
< end;
< overflow("var size",hi_mem_base-mem_base); {sorry, nothing satisfactory is left}
< found: link(r)←null; {this node is now nonempty}
< stat var_used←var_used+s; {maintain usage statistics}
< if var_used>max_var_used then max_var_used←var_used;
---
> begin get_node:=max_halfword; return;
> end;
> if lo_mem_max+2<hi_mem_min then if lo_mem_max+2<=mem_bot+max_halfword then
> @<Grow more variable-size memory and |goto restart|@>;
> overflow("main memory size",mem_max+1-mem_min);
> {sorry, nothing satisfactory is left}
> @:TeX capacity exceeded main memory size}{\quad main memory size@>
> found: link(r):=null; {this node is now nonempty}
> @!stat var_used:=var_used+s; {maintain usage statistics}
2288c2628
< get_node←r;
---
> get_node:=r;
2291,2292c2631,2666
< @ The following operations remove an empty node from the doubly-linked
< list, knowing that it is not the only item in the list.
---
> @ The lower part of |mem| grows by 1000 words at a time, unless
> we are very close to going under. When it grows, we simply link
> a new node into the available-space list. This method of controlled
> growth helps to keep the |mem| usage consecutive when \TeX\ is
> implemented on ``virtual memory'' systems.
> @^virtual memory@>
>
> @<Grow more variable-size memory and |goto restart|@>=
> begin if hi_mem_min-lo_mem_max>=1998 then t:=lo_mem_max+1000
> else t:=lo_mem_max+1+(hi_mem_min-lo_mem_max) div 2;
> {|lo_mem_max+2<=t<hi_mem_min|}
> p:=llink(rover); q:=lo_mem_max; rlink(p):=q; llink(rover):=q;@/
> if t>mem_bot+max_halfword then t:=mem_bot+max_halfword;
> rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/
> lo_mem_max:=t; link(lo_mem_max):=null; info(lo_mem_max):=null;
> rover:=q; goto restart;
> end
>
> @ Empirical tests show that the routine in this section performs a
> node-merging operation about 0.75 times per allocation, on the average,
> after which it finds that |r>p+1| about 95\pct! of the time.
>
> @<Try to allocate...@>=
> q:=p+node_size(p); {find the physical successor}
> @^inner loop@>
> while is_empty(q) do {merge node |p| with node |q|}
> begin t:=rlink(q);
> if q=rover then rover:=t;
> llink(t):=llink(q); rlink(llink(q)):=t;@/
> q:=q+node_size(q);
> end;
> r:=q-s;
> if r>p+1 then @<Allocate from the top of node |p| and |goto found|@>;
> if r=p then if rlink(p)<>p then
> @<Allocate entire node |p| and |goto found|@>;
> node_size(p):=q-p {reset the size in case it grew}
2294,2312c2668,2671
< @d remove_node(#) ==@;
< if #=rover then rover←rlink(#);
< llink(rlink(#))←llink(#);
< rlink(llink(#))←rlink(#)
<
< @ @<Try to allocate...@>=
< q←p+node_size(p); {find the physical successor}
< while is_empty(q) do {merge |p| with |q|}
< begin remove_node(q); q←q+node_size(q);
< end;
< r←q-s;
< if r>p+1 then @<Allocate from top of node |p| and |goto found|@>;
< if (r=p) and ((rlink(p)≠rover) or (llink(p)≠rover)) then
< @<Allocate entire node |p| and |goto found|@>;
< node_size(p)←q-p {reset the size in case it grew}
<
< @ @<Allocate from top...@>=
< begin node_size(p)←r-p; {store the remaining size}
< rover←p; {start searching here next time}
---
> @ @<Allocate from the top...@>=
> begin node_size(p):=r-p; {store the remaining size}
> @^inner loop@>
> rover:=p; {start searching here next time}
2316,2318c2675,2679
< @ @<Allocate entire...@>=
< begin remove_node(p); {delete node |p| from the ring}
< rover←rlink(p); {let |rover| rove around}
---
> @ Here we delete node |p| from the ring, and let |rover| rove around.
>
> @<Allocate entire...@>=
> begin rover:=rlink(p); t:=llink(p);
> llink(rover):=t; rlink(t):=rover;
2324a2686
> @^inner loop@>
2327c2689
< liberation}
---
> liberation}
2329,2335c2691,2694
< begin if @<Node |p| isn't in the variable-size |mem|@> then
< confusion("freenode");
< @:confusion free_node}{\quad freenode@>
< node_size(p)←s; link(p)←empty_flag;
< q←llink(rover); llink(p)←q; rlink(p)←rover; {set both links}
< llink(rover)←p; rlink(q)←p; {insert |p| into the ring}
< stat var_used←var_used-s;@+tats@;{maintain statistics}
---
> begin node_size(p):=s; link(p):=empty_flag;
> q:=llink(rover); llink(p):=q; rlink(p):=rover; {set both links}
> llink(rover):=p; rlink(q):=p; {insert |p| into the ring}
> @!stat var_used:=var_used-s;@+tats@;{maintain statistics}
2343,2344c2702,2703
< @p init procedure sort_avail; {sorts the available variable-size nodes
< by location}
---
> @p @!init procedure sort_avail; {sorts the available variable-size nodes
> by location}
2347,2355c2706,2714
< begin p←get_node(@'10000000000); {merge adjacent free areas}
< p←rlink(rover); rlink(rover)←max_halfword; old_rover←rover;
< while p≠old_rover do @<Sort |p| into the list starting at |rover|
< and advance |p| to |rlink(p)|@>;
< p←rover;
< while rlink(p)≠max_halfword do
< begin llink(rlink(p))←p; p←rlink(p);
< end;
< rlink(p)←rover; llink(rover)←p;
---
> begin p:=get_node(@'10000000000); {merge adjacent free areas}
> p:=rlink(rover); rlink(rover):=max_halfword; old_rover:=rover;
> while p<>old_rover do @<Sort \(p)|p| into the list starting at |rover|
> and advance |p| to |rlink(p)|@>;
> p:=rover;
> while rlink(p)<>max_halfword do
> begin llink(rlink(p)):=p; p:=rlink(p);
> end;
> rlink(p):=rover; llink(rover):=p;
2359c2718,2719
< @ The following |while| loop terminates, since the list that starts at
---
> @ The following |while| loop is guaranteed to
> terminate, since the list that starts at
2362c2722
< @<Sort |p|...@>=
---
> @<Sort \(p)|p|...@>=
2364,2369c2724,2730
< begin q←p; p←rlink(q); rlink(q)←rover; rover←q;
< end
< else begin q←rover;
< while rlink(q)<p do q←rlink(q);
< r←rlink(p); rlink(p)←rlink(q); rlink(q)←p; p←r;
< end
---
> begin q:=p; p:=rlink(q); rlink(q):=rover; rover:=q;
> end
> else begin q:=rover;
> while rlink(q)<p do q:=rlink(q);
> r:=rlink(p); rlink(p):=rlink(q); rlink(q):=p; p:=r;
> end
>
2390c2751
< two words, and they appear in |mem| locations less than |hi_mem_base|.
---
> two words, and they appear in |mem| locations less than |hi_mem_min|.
2398,2400c2759,2761
< and most fonts will stick to characters less than 128 (since higher codes
< are accessed outside of math mode only via ligatures and the \.{\\char}
< operator).
---
> and most fonts will stick to characters whose codes are
> less than 128 (since higher codes
> are more difficult to access on most keyboards).
2407,2410c2768,2770
< entries: The first of these would identify the font and the character
< dimensions, and it would also link to the second, where the full halfword
< |info| field would address the desired character. Such an extension of
< \TeX\ would not be difficult; further details are left to the reader.
---
> entries: The first of these has |font=font_base|, and its |link| points
> to the second;
> the second identifies the font and the character dimensions.
2412c2772,2776
< the same box dimensions.
---
> the same box dimensions. The |character| field of the first |char_node|
> is a ``\\{charext}'' that distinguishes between graphic symbols whose
> dimensions are identical for typesetting purposes. (See the \MF\ manual.)
> Such an extension of \TeX\ would not be difficult; further details are
> left to the reader.
2419,2420c2783,2784
< @d is_char_node(#) == (#>hi_mem_base)
< {does the argument point to a |char_node|?}
---
> @d is_char_node(#) == (#>=hi_mem_min)
> {does the argument point to a |char_node|?}
2458c2822,2824
< @d glue_set(#) == mem[#+6].gr {a word of type |glue_ratio| for glue setting}
---
> @d glue_offset = 6 {position of |glue_set| in a box node}
> @d glue_set(#) == mem[#+glue_offset].gr
> {a word of type |glue_ratio| for glue setting}
2462,2463c2828,2829
< The |subtype| field is set to |min_quarterword|, since that is the |span_count|
< value that is desired in case this |hlist_node| is changed to an |unset_node|.
---
> The |subtype| field is set to |min_quarterword|, since that's the desired
> |span_count| value if this |hlist_node| is changed to an |unset_node|.
2467,2471c2833,2837
< begin p←get_node(box_node_size); type(p)←hlist_node;
< subtype(p)←min_quarterword;
< width(p)←0; depth(p)←0; height(p)←0; shift_amount(p)←0; list_ptr(p)←null;
< glue_sign(p)←normal; glue_order(p)←normal; glue_set(p)←0.0;
< new_null_box←p;
---
> begin p:=get_node(box_node_size); type(p):=hlist_node;
> subtype(p):=min_quarterword;
> width(p):=0; depth(p):=0; height(p):=0; shift_amount(p):=0; list_ptr(p):=null;
> glue_sign(p):=normal; glue_order(p):=normal; set_glue_ratio_zero(glue_set(p));
> new_null_box:=p;
2475c2841
< was made from a vertical list.
---
> contains a vertical list.
2484c2850
< an hlist; the |height| and |depth| are never running in a vlist.
---
> an hlist; the |height| and |depth| are never running in a~vlist.
2497,2500c2863,2866
< begin p←get_node(rule_node_size); type(p)←rule_node;
< subtype(p)←0; {the |subtype| is not used}
< width(p)←null_flag; depth(p)←null_flag; height(p)←null_flag;
< new_rule←p;
---
> begin p:=get_node(rule_node_size); type(p):=rule_node;
> subtype(p):=0; {the |subtype| is not used}
> width(p):=null_flag; depth(p):=null_flag; height(p):=null_flag;
> new_rule:=p;
2506c2872
< The |width| field of an |ins_node| is slightly misnamed; it actually holds
---
> The |height| field of an |ins_node| is slightly misnamed; it actually holds
2508,2510c2874,2879
< There is one more field, the |ins_ptr|, which points to the beginning of
< the vlist for the insertion. In the present implementation, |ins_ptr|
< occupies a full word, although half a word would be enough.
---
> The |depth| field holds the |split_max_depth| to be used in case this
> insertion is split, and the |split_top_ptr| points to the corresponding
> |split_top_skip|. The |float_cost| field holds the |floating_penalty| that
> will be used if this insertion floats to a subsequent page after a
> split insertion of the same class. There is one more field, the
> |ins_ptr|, which points to the beginning of the vlist for the insertion.
2513,2514c2882,2885
< @d ins_node_size=3 {number of words to allocate for an insertion}
< @d ins_ptr(#)==mem[#+2].int {the vertical list to be inserted}
---
> @d ins_node_size=5 {number of words to allocate for an insertion}
> @d float_cost(#)==mem[#+1].int {the |floating_penalty| to be used}
> @d ins_ptr(#)==info(#+4) {the vertical list to be inserted}
> @d split_top_ptr(#)==link(#+4) {the |split_top_skip| to be used}
2517,2518c2888,2889
< of a token list that contains the user's \.{\\mark} text. Like the |ins_ptr|
< field, this one occupies a full word instead of a halfword just because
---
> of a token list that contains the user's \.{\\mark} text.
> This field occupies a full word instead of a halfword, because
2526,2529c2897,2901
< @ An |adjust_node|, which occurs only in horizontal lists, specifies
< material that will be moved out into the surrounding vertical list;
< i.e., it is used to implement \TeX's `\.{\\vadjust}' operation.
< The |adjust_ptr| field points to the vlist containing this material.
---
> @ An |adjust_node|, which occurs only in horizontal lists,
> specifies material that will be moved out into the surrounding
> vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}'
> operation. The |adjust_ptr| field points to the vlist containing this
> material.
2534,2540c2906,2918
< @ A |ligature_node|, which occurs only in horizontal lists, specifies a
< composite character that was formed from two or more actual characters.
< The second word of the node, which is called the |lig_char| word, contains
< |font| and |character| fields just as in a |char_node|. The characters
< that generated the ligature have not been forgotten, since they are needed
< for diagnostic messages and for hyphenation; the |lig_ptr| field points to
< a linked list of character nodes for those characters.
---
> @ A |ligature_node|, which occurs only in horizontal lists, specifies
> a character that was fabricated from the interaction of two or more
> actual characters. The second word of the node, which is called the
> |lig_char| word, contains |font| and |character| fields just as in a
> |char_node|. The characters that generated the ligature have not been
> forgotten, since they are needed for diagnostic messages and for
> hyphenation; the |lig_ptr| field points to a linked list of character
> nodes for all original characters that have been deleted. (This list
> might be empty if the characters that generated the ligature were
> retained in other nodes.)
>
> The |subtype| field is 0, plus 2 and/or 1 if the original source of the
> ligature included implicit left and/or right boundaries.
2547c2925,2928
< contents of the |font|, |character|, and |lig_ptr| fields.
---
> contents of the |font|, |character|, and |lig_ptr| fields. We also have
> a |new_lig_item| function, which returns a two-word node having a given
> |character| field. Such nodes are used for temporary processing as ligatures
> are being created.
2551,2554c2932,2940
< begin p←get_node(small_node_size); type(p)←ligature_node;
< subtype(p)←0; {the |subtype| is not used}
< font(lig_char(p))←f; character(lig_char(p))←c; lig_ptr(p)←q;
< new_ligature←p;
---
> begin p:=get_node(small_node_size); type(p):=ligature_node;
> font(lig_char(p)):=f; character(lig_char(p)):=c; lig_ptr(p):=q;
> subtype(p):=0; new_ligature:=p;
> end;
> @#
> function new_lig_item(@!c:quarterword):pointer;
> var p:pointer; {the new node}
> begin p:=get_node(small_node_size); character(p):=c; lig_ptr(p):=null;
> new_lig_item:=p;
2565c2951
< lists that consist entirely of character, kern, and ligature nodes.
---
> lists that consist entirely of character, kern, box, rule, and ligature nodes.
2581,2583c2967,2969
< begin p←get_node(small_node_size); type(p)←disc_node;
< replace_count(p)←0; pre_break(p)←null; post_break(p)←null;
< new_disc←p;
---
> begin p:=get_node(small_node_size); type(p):=disc_node;
> replace_count(p):=0; pre_break(p):=null; post_break(p):=null;
> new_disc:=p;
2592c2978
< things by adding code to these extension modules. For example, there
---
> things by adding code at the end of the program. For example, there
2598c2984
< `\.{\\send}' and `\.{\\xsend}' as if they were extensions, in order to
---
> `\.{\\write}' and `\.{\\special}' as if they were extensions, in order to
2615,2616c3001,3002
< begin p←get_node(small_node_size); type(p)←math_node;
< subtype(p)←s; width(p)←w; new_math←p;
---
> begin p:=get_node(small_node_size); type(p):=math_node;
> subtype(p):=s; width(p):=w; new_math:=p;
2623,2624c3009,3010
< |type| of the previous node is |math_node| or less. Furthermore, a
< node is discarded after a break if its type is |math_node| or more.
---
> |type| of the previous node is less than |math_node|. Furthermore, a
> node is discarded after a break if its type is |math_node| or~more.
2626c3012
< @d precedes_break(#)==(type(#)≤math_node)
---
> @d precedes_break(#)==(type(#)<math_node)
2647c3033
< will be the same as the parameter number, plus one.
---
> will be the same as the parameter number, plus~one.
2654c3040
<
---
>
2656,2657c3042,3043
< @d mu_glue=98 {|subtype| for mskip glue}
< @d cond_math_glue=99 {special |subtype| to suppress glue in the next node}
---
> @d cond_math_glue=98 {special |subtype| to suppress glue in the next node}
> @d mu_glue=99 {|subtype| for math glue}
2691a3078,3079
> The reference count in the copy is |null|, because there is assumed
> to be exactly one reference to the new specification.
2695,2698c3083,3086
< begin q←get_node(glue_spec_size);@/
< mem[q]←mem[p]; glue_ref_count(q)←null;@/
< width(q)←width(p); stretch(q)←stretch(p); shrink(q)←shrink(p);
< new_spec←q;
---
> begin q:=get_node(glue_spec_size);@/
> mem[q]:=mem[p]; glue_ref_count(q):=null;@/
> width(q):=width(p); stretch(q):=stretch(p); shrink(q):=shrink(p);
> new_spec:=q;
2701,2703c3089,3092
< @ And here's a function that creates a glue node for a given parameter;
< for example, |new_param_glue(disp_skip_code)| returns a pointer to a glue
< node for the current \.{\\dispskip}.
---
> @ And here's a function that creates a glue node for a given parameter
> identified by its code number; for example,
> |new_param_glue(line_skip_code)| returns a pointer to a glue node for the
> current \.{\\lineskip}.
2708,2712c3097,3101
< begin p←get_node(small_node_size); type(p)←glue_node; subtype(p)←n+1;
< leader_ptr(p)←null;@/
< q←@<Current |mem| equivalent of glue parameter number |n|@>@t@>;
< glue_ptr(p)←q; incr(glue_ref_count(q));
< new_param_glue←p;
---
> begin p:=get_node(small_node_size); type(p):=glue_node; subtype(p):=n+1;
> leader_ptr(p):=null;@/
> q:=@<Current |mem| equivalent of glue parameter number |n|@>@t@>;
> glue_ptr(p):=q; incr(glue_ref_count(q));
> new_param_glue:=p;
2720,2722c3109,3111
< begin p←get_node(small_node_size); type(p)←glue_node; subtype(p)←normal;
< leader_ptr(p)←null; glue_ptr(p)←q; incr(glue_ref_count(q));
< new_glue←p;
---
> begin p:=get_node(small_node_size); type(p):=glue_node; subtype(p):=normal;
> leader_ptr(p):=null; glue_ptr(p):=q; incr(glue_ref_count(q));
> new_glue:=p;
2725c3114
< @ Still another subroutine is needed: this one is sort of a combination
---
> @ Still another subroutine is needed: This one is sort of a combination
2734,2736c3123,3125
< begin temp_ptr←new_spec(@<Current |mem| equivalent of glue parameter...@>);
< p←new_glue(temp_ptr); glue_ref_count(temp_ptr)←null; subtype(p)←n+1;
< new_skip_param←p;
---
> begin temp_ptr:=new_spec(@<Current |mem| equivalent of glue parameter...@>);
> p:=new_glue(temp_ptr); glue_ref_count(temp_ptr):=null; subtype(p):=n+1;
> new_skip_param:=p;
2744,2746c3133,3137
< spacing in the vertical direction. The |subtype| is either |normal| or
< |mu_glue|, where the latter is used for \.{\\mkern} specifications
< in math formulas.
---
> spacing in the vertical direction. The |subtype| is either |normal| (for
> kerns inserted from font information or math mode calculations) or |explicit|
> (for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern|
> (for kerns inserted from non-math accents) or |mu_glue| (for kerns
> inserted from \.{\\mkern} specifications in math formulas).
2748a3140,3141
> @d explicit=1 {|subtype| of kern nodes from \.{\\kern} and \.{\\/}}
> @d acc_kern=2 {|subtype| of kern nodes from accents}
2754,2757c3147,3150
< begin p←get_node(small_node_size); type(p)←kern_node;
< subtype(p)←normal;
< width(p)←w;
< new_kern←p;
---
> begin p:=get_node(small_node_size); type(p):=kern_node;
> subtype(p):=normal;
> width(p):=w;
> new_kern:=p;
2762c3155
< the full range of integer values is not used: Any penalty |≥10000| is
---
> the full range of integer values is not used: Any penalty |>=10000| is
2764c3157
< Similarly, any penalty |≤-10000| is treated as negative infinity, and a
---
> Similarly, any penalty |<=-10000| is treated as negative infinity, and a
2777,2779c3170,3172
< begin p←get_node(small_node_size); type(p)←penalty_node;
< subtype(p)←0; {the |subtype| is not used}
< penalty(p)←m; new_penalty←p;
---
> begin p:=get_node(small_node_size); type(p):=penalty_node;
> subtype(p):=0; {the |subtype| is not used}
> penalty(p):=m; new_penalty:=p;
2797c3190
< @d glue_stretch(#)==mem[#+6].sc {total stretch in an unset node}
---
> @d glue_stretch(#)==mem[#+glue_offset].sc {total stretch in an unset node}
2808,2810c3201,3206
< below may have to be changed. However, other references to the nodes are made
< symbolically in terms of the \.{WEB} macro definitions above, so format changes
< will leave \TeX's other algorithms intact.
---
> below may have to be changed. Such potentially dangerous parts of the
> program are listed in the index under `data structure assumptions'.
> @!@^data structure assumptions@>
> However, other references to the nodes are made symbolically in terms of
> the \.{WEB} macro definitions above, so that format changes will leave
> \TeX's other algorithms intact.
2811a3208
>
2815c3212
< example, locations |mem_base| to |mem_base+3| are always used to store the
---
> example, locations |mem_bot| to |mem_bot+3| are always used to store the
2818,2821c3215,3218
< symbolic names to the fixed positions. Dynamic allocation of variable-size
< nodes is restricted to locations |first_mem| through |(hi_mem_base-1)|,
< and single-word nodes are dynamically allocated in locations |second_mem|
< through |mem_max|, inclusive.
---
> symbolic names to the fixed positions. Static variable-size nodes appear
> in locations |mem_bot| through |lo_mem_stat_max|, and static single-word nodes
> appear in locations |hi_mem_stat_min| through |mem_top|, inclusive. It is
> harmless to let |lig_trick| and |garbage| share the same location of |mem|.
2823c3220
< @d zero_glue==mem_base {specification for \.{0pt plus 0pt minus 0pt}}
---
> @d zero_glue==mem_bot {specification for \.{0pt plus 0pt minus 0pt}}
2828,2829c3225,3226
< @d first_mem==fil_neg_glue+glue_spec_size {first dynamically allocatable word
< in the variable-size |mem|}
---
> @d lo_mem_stat_max==fil_neg_glue+glue_spec_size-1 {largest statically
> allocated word in the variable-size |mem|}
2831,2847c3228,3244
< @d page_ins_head==hi_mem_base {list of insertion data for current page}
< @d contrib_head==hi_mem_base+1 {vlist of items not yet on current page}
< @d page_head==hi_mem_base+2 {vlist for current page}
< @d temp_head==hi_mem_base+3 {head of a temporary list of some kind}
< @d hold_head==hi_mem_base+4 {head of a temporary list of another kind}
< @d scan_head==hi_mem_base+5 {head of token list built by |scan_keyword|}
< @d adjust_head==hi_mem_base+6 {head of adjustment list returned by |hpack|}
< @d active==hi_mem_base+7 {head of active list in |line_break|, needs two words}
< @d align_head==hi_mem_base+9 {head of preamble list for alignments}
< @d end_span==hi_mem_base+10 {tail of spanned-width lists}
< @d omit_template==hi_mem_base+11 {a constant token list}
< @d lig_trick==hi_mem_base+12 {a ligature masquerading as a |char_node|}
< @d garbage==hi_mem_base+13 {used for scrap information}
< @d second_mem==hi_mem_base+14 {first dynamically allocatable word in
< the one-word |mem|}
<
< @<Node |p| isn't in the variable-size |mem|@>=(p<first_mem)∨(p≥hi_mem_base)
---
> @d page_ins_head==mem_top {list of insertion data for current page}
> @d contrib_head==mem_top-1 {vlist of items not yet on current page}
> @d page_head==mem_top-2 {vlist for current page}
> @d temp_head==mem_top-3 {head of a temporary list of some kind}
> @d hold_head==mem_top-4 {head of a temporary list of another kind}
> @d adjust_head==mem_top-5 {head of adjustment list returned by |hpack|}
> @d active==mem_top-7 {head of active list in |line_break|, needs two words}
> @d align_head==mem_top-8 {head of preamble list for alignments}
> @d end_span==mem_top-9 {tail of spanned-width lists}
> @d omit_template==mem_top-10 {a constant token list}
> @d null_list==mem_top-11 {permanently empty list}
> @d lig_trick==mem_top-12 {a ligature masquerading as a |char_node|}
> @d garbage==mem_top-12 {used for scrap information}
> @d backup_head==mem_top-13 {head of token list built by |scan_keyword|}
> @d hi_mem_stat_min==mem_top-13 {smallest statically allocated word in
> the one-word |mem|}
> @d hi_mem_stat_usage=14 {the number of one-word nodes always present}
2850c3247
< initializing itself the slow way.
---
> initializing itself the slow~way.
2856,2873c3253,3273
< for k←mem_base+1 to first_mem-1 do mem[k].sc←0;
< {all glue dimensions are zeroed}
< k←mem_base;@+while k<first_mem do {set first words of glue specifications}
< begin glue_ref_count(k)←null+1;
< stretch_order(k)←normal; shrink_order(k)←normal;
< k←k+glue_spec_size;
< end;
< stretch(fil_glue)←unity; stretch_order(fil_glue)←fil;@/
< stretch(fill_glue)←unity; stretch_order(fill_glue)←fill;@/
< stretch(ss_glue)←unity; stretch_order(ss_glue)←fil;@/
< shrink(ss_glue)←unity; shrink_order(ss_glue)←fil;@/
< stretch(fil_neg_glue)←-unity; stretch_order(fil_neg_glue)←fil;@/
< rover←first_mem; link(rover)←empty_flag; {now initialize the dynamic memory}
< node_size(rover)←hi_mem_base-rover; {which is one big available node}
< llink(rover)←rover; rlink(rover)←rover;@/
< link(hi_mem_base)←null; info(hi_mem_base)←null;
< for k←hi_mem_base+1 to second_mem-1 do
< mem[k]←mem[hi_mem_base];{clear list heads}
---
> for k:=mem_bot+1 to lo_mem_stat_max do mem[k].sc:=0;
> {all glue dimensions are zeroed}
> @^data structure assumptions@>
> k:=mem_bot;@+while k<=lo_mem_stat_max do
> {set first words of glue specifications}
> begin glue_ref_count(k):=null+1;
> stretch_order(k):=normal; shrink_order(k):=normal;
> k:=k+glue_spec_size;
> end;
> stretch(fil_glue):=unity; stretch_order(fil_glue):=fil;@/
> stretch(fill_glue):=unity; stretch_order(fill_glue):=fill;@/
> stretch(ss_glue):=unity; stretch_order(ss_glue):=fil;@/
> shrink(ss_glue):=unity; shrink_order(ss_glue):=fil;@/
> stretch(fil_neg_glue):=-unity; stretch_order(fil_neg_glue):=fil;@/
> rover:=lo_mem_stat_max+1;
> link(rover):=empty_flag; {now initialize the dynamic memory}
> node_size(rover):=1000; {which is a 1000-word available node}
> llink(rover):=rover; rlink(rover):=rover;@/
> lo_mem_max:=rover+1000; link(lo_mem_max):=null; info(lo_mem_max):=null;@/
> for k:=hi_mem_stat_min to mem_top do
> mem[k]:=mem[lo_mem_max]; {clear list heads}
2875,2877c3275,3278
< avail←null; mem_end←second_mem-1; {initialize the one-word memory}
< var_used←first_mem-mem_base; dyn_used←second_mem-hi_mem_base;
< max_var_used←var_used; {initialize statistics}
---
> avail:=null; mem_end:=mem_top;
> hi_mem_min:=hi_mem_stat_min; {initialize the one-word memory}
> var_used:=lo_mem_stat_max+1-mem_bot; dyn_used:=hi_mem_stat_usage;
> {initialize statistics}
2887,2888c3288
< are debugging; decreasing |mem_max| saves space and time,
< and decreasing |hi_mem_base| saves time.)
---
> are debugging.)
2890,2895c3290,3296
< @<Globals...@>=
< debug @!free: packed array [mem_base..mem_max] of boolean; {free cells}
< @t\hskip1em@>@!was_free: packed array [mem_base..mem_max] of boolean;
< {previously free cells}
< @t\hskip1em@>@!was_mem_end: pointer; {previous |mem_end|}
< @t\hskip1em@>@!panicking:boolean; {do we want to check memory constantly?}
---
> @<Glob...@>=
> @!debug @!free: packed array [mem_min..mem_max] of boolean; {free cells}
> @t\hskip10pt@>@!was_free: packed array [mem_min..mem_max] of boolean;
> {previously free cells}
> @t\hskip10pt@>@!was_mem_end,@!was_lo_max,@!was_hi_min: pointer;
> {previous |mem_end|, |lo_mem_max|, and |hi_mem_min|}
> @t\hskip10pt@>@!panicking:boolean; {do we want to check memory constantly?}
2899,2900c3300,3302
< debug was_mem_end←mem_base; {indicate that everything was previously free}
< panicking←false;
---
> @!debug was_mem_end:=mem_min; {indicate that everything was previously free}
> was_lo_max:=mem_min; was_hi_min:=mem_max;
> panicking:=false;
2907c3309
< @p debug procedure check_mem(@!print_locs : boolean);
---
> @p @!debug procedure check_mem(@!print_locs : boolean);
2911,2912c3313,3315
< begin for p←mem_base to mem_end do free[p]←false; {you can probably
< do this faster}
---
> begin for p:=mem_min to lo_mem_max do free[p]:=false; {you can probably
> do this faster}
> for p:=hi_mem_min to mem_end do free[p]:=false; {ditto}
2917,2918c3320,3323
< for p←0 to mem_end do was_free[p]←free[p]; {|was_free←free| might be faster}
< was_mem_end←mem_end;
---
> for p:=mem_min to lo_mem_max do was_free[p]:=free[p];
> for p:=hi_mem_min to mem_end do was_free[p]:=free[p];
> {|was_free:=free| might be faster}
> was_mem_end:=mem_end; was_lo_max:=lo_mem_max; was_hi_min:=hi_mem_min;
2923,2932c3328,3338
< p←avail; q←null; clobbered←false;
< while p≠null do
< begin if (p>mem_end)∨(p<second_mem) then clobbered←true
< else if free[p] then clobbered←true;
< if clobbered then
< begin print_nl("AVAIL list clobbered at ");
< print_int(q); goto done1;
< end;
< free[p]←true; q←p; p←link(q);
< end;
---
> p:=avail; q:=null; clobbered:=false;
> while p<>null do
> begin if (p>mem_end)or(p<hi_mem_min) then clobbered:=true
> else if free[p] then clobbered:=true;
> if clobbered then
> begin print_nl("AVAIL list clobbered at ");
> @.AVAIL list clobbered...@>
> print_int(q); goto done1;
> end;
> free[p]:=true; q:=p; p:=link(q);
> end;
2936,2952c3342,3359
< p←rover; q←null; clobbered←false;
< repeat if (p≥hi_mem_base)∨(p<first_mem) then clobbered←true
< else if (rlink(p)≥hi_mem_base)∨(rlink(p)<first_mem) then clobbered←true
< else if ¬(is_empty(p))∨(node_size(p)<2)∨@|
< (p+node_size(p)>hi_mem_base)∨@| (llink(rlink(p))≠p) then clobbered←true;
< if clobbered then
< begin print_nl("Double-AVAIL list clobbered at ");
< print_int(q); goto done2;
< end;
< for q←p to p+node_size(p)-1 do {mark all locations free}
< begin if free[q] then
< begin print_nl("Doubly free location at ");
< print_int(q); goto done2;
< end;
< free[q]←true;
< end;
< q←p; p←rlink(p);
---
> p:=rover; q:=null; clobbered:=false;
> repeat if (p>=lo_mem_max)or(p<mem_min) then clobbered:=true
> else if (rlink(p)>=lo_mem_max)or(rlink(p)<mem_min) then clobbered:=true
> else if not(is_empty(p))or(node_size(p)<2)or@|
> (p+node_size(p)>lo_mem_max)or@| (llink(rlink(p))<>p) then clobbered:=true;
> if clobbered then
> begin print_nl("Double-AVAIL list clobbered at ");
> print_int(q); goto done2;
> end;
> for q:=p to p+node_size(p)-1 do {mark all locations free}
> begin if free[q] then
> begin print_nl("Doubly free location at ");
> @.Doubly free location...@>
> print_int(q); goto done2;
> end;
> free[q]:=true;
> end;
> q:=p; p:=rlink(p);
2957,2964c3364,3372
< p←mem_base;
< while p≤hi_mem_base do {node |p| should not be empty}
< begin if is_empty(p) then
< begin print_nl("Bad flag at "); print_int(p);
< end;
< while (p≤hi_mem_base) and not free[p] do incr(p);
< while (p≤hi_mem_base) and free[p] do incr(p);
< end
---
> p:=mem_min;
> while p<=lo_mem_max do {node |p| should not be empty}
> begin if is_empty(p) then
> begin print_nl("Bad flag at "); print_int(p);
> @.Bad flag...@>
> end;
> while (p<=lo_mem_max) and not free[p] do incr(p);
> while (p<=lo_mem_max) and free[p] do incr(p);
> end
2968,2971c3376,3384
< for p←mem_base to mem_end do
< if not free[p] and ((p>was_mem_end) or was_free[p]) then
< begin print_char(" "); print_int(p);
< end;
---
> for p:=mem_min to lo_mem_max do
> if not free[p] and ((p>was_lo_max) or was_free[p]) then
> begin print_char(" "); print_int(p);
> end;
> for p:=hi_mem_min to mem_end do
> if not free[p] and
> ((p<was_hi_min) or (p>was_mem_end) or was_free[p]) then
> begin print_char(" "); print_int(p);
> end;
2975c3388
< to node@@|p|?'' In doing so, it fetches |link| and |info| fields of |mem|
---
> to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem|
2983,2993c3396,3416
< @p debug procedure search_mem(@!p:pointer); {look for pointers to |p|}
< var q:pointer; {current position being searched}
< begin for q←mem_base to mem_end do
< begin if link(q)=p then
< begin print_nl("LINK("); print_int(q); print_char(")");
< end;
< if info(q)=p then
< begin print_nl("INFO("); print_int(q); print_char(")");
< end;
< end;
< @<Search |eqtb| for equivalents equal to |p|@>;
---
> @p @!debug procedure search_mem(@!p:pointer); {look for pointers to |p|}
> var q:integer; {current position being searched}
> begin for q:=mem_min to lo_mem_max do
> begin if link(q)=p then
> begin print_nl("LINK("); print_int(q); print_char(")");
> end;
> if info(q)=p then
> begin print_nl("INFO("); print_int(q); print_char(")");
> end;
> end;
> for q:=hi_mem_min to mem_end do
> begin if link(q)=p then
> begin print_nl("LINK("); print_int(q); print_char(")");
> end;
> if info(q)=p then
> begin print_nl("INFO("); print_int(q); print_char(")");
> end;
> end;
> @<Search |eqtb| for equivalents equal to |p|@>;
> @<Search |save_stack| for equivalents that point to |p|@>;
> @<Search |hyph_list| for pointers to |p|@>;
2995a3419
>
3022,3037c3446,3461
< begin while p>null do
< begin if is_char_node(p) then
< begin if p≤mem_end then
< begin if font(p)≠font_in_short_display then
< begin print_esc(":");
< if (font(p)<0)∨(font(p)>font_max) then
< print_char("*")
< else print_int(font_code[font(p)]);
< print_char(" "); font_in_short_display←font(p);
< end;
< print_ascii(qo(character(p)));
< end;
< end
< else @<Print a short indication of the contents of node |p|@>;
< p←link(p);
< end;
---
> begin while p>mem_min do
> begin if is_char_node(p) then
> begin if p<=mem_end then
> begin if font(p)<>font_in_short_display then
> begin if (font(p)<font_base)or(font(p)>font_max) then
> print_char("*")
> @.*\relax@>
> else @<Print the font identifier for |font(p)|@>;
> print_char(" "); font_in_short_display:=font(p);
> end;
> print_ASCII(qo(character(p)));
> end;
> end
> else @<Print a short indication of the contents of node |p|@>;
> p:=link(p);
> end;
3039c3463
<
---
>
3043c3467
< unset_node: print("[]");
---
> unset_node: print("[]");
3045c3469
< glue_node: if glue_ptr(p)≠zero_glue then print_char(" ");
---
> glue_node: if glue_ptr(p)<>zero_glue then print_char(" ");
3049,3054c3473,3479
< short_display(post_break(p));@/
< n←replace_count(p);
< while n>0 do
< begin p←link(p); decr(n);
< end;
< end;
---
> short_display(post_break(p));@/
> n:=replace_count(p);
> while n>0 do
> begin if link(p)<>null then p:=link(p);
> decr(n);
> end;
> end;
3064,3068c3489,3493
< else begin print_esc(":");
< if (font(p)<0)∨(font(p)>font_max) then print_char("*")
< else print_int(font_code[font(p)]);
< print_char(" "); print_ascii(qo(character(p)));
< end;
---
> else begin if (font(p)<font_base)or(font(p)>font_max) then print_char("*")
> @.*\relax@>
> else @<Print the font identifier for |font(p)|@>;
> print_char(" "); print_ASCII(qo(character(p)));
> end;
3073c3498
< if (p<hi_mem_base)∨(p>mem_end) then print_esc("CLOBBERED.")
---
> if (p<hi_mem_min)or(p>mem_end) then print_esc("CLOBBERED.")
3079a3505
> @.*\relax@>
3086c3512
< {prints a glue component}
---
> {prints a glue component}
3088c3514
< if (order<normal)∨(order>filll) then print("foul")
---
> if (order<normal)or(order>filll) then print("foul")
3090,3095c3516,3521
< begin print("fil");
< while order>fil do
< begin print_char("l"); decr(order);
< end;
< end
< else if s≠0 then print(s);
---
> begin print("fil");
> while order>fil do
> begin print_char("l"); decr(order);
> end;
> end
> else if s<>0 then print(s);
3100,3111c3526,3538
< @p procedure print_spec(@!p:pointer;@!s:str_number);
< {prints a glue specification}
< begin if (p<mem_base)∨(p≥hi_mem_base) then print_char("*")
< else begin print_scaled(width(p));
< if s≠0 then print(s);
< if stretch(p)≠0 then
< begin print(" plus "); print_glue(stretch(p),stretch_order(p),s);
< end;
< if shrink(p)≠0 then
< begin print(" minus "); print_glue(shrink(p),shrink_order(p),s);
< end;
< end;
---
> @p procedure print_spec(@!p:integer;@!s:str_number);
> {prints a glue specification}
> begin if (p<mem_min)or(p>=lo_mem_max) then print_char("*")
> @.*\relax@>
> else begin print_scaled(width(p));
> if s<>0 then print(s);
> if stretch(p)<>0 then
> begin print(" plus "); print_glue(stretch(p),stretch_order(p),s);
> end;
> if shrink(p)<>0 then
> begin print(" minus "); print_glue(shrink(p),shrink_order(p),s);
> end;
> end;
3115c3542
< docu\-mentation.
---
> documentation.
3129,3130c3556,3557
< begin append_char("."); show_node_list(#); flush_char;
< end {|str_room| need not be checked; see |show_box| below}
---
> begin append_char("."); show_node_list(#); flush_char;
> end {|str_room| need not be checked; see |show_box| below}
3139c3566
< @<Globals...@>=
---
> @<Glob...@>=
3155c3582
< @p procedure show_node_list(@!p:pointer); {prints a node list symbolically}
---
> @p procedure show_node_list(@!p:integer); {prints a node list symbolically}
3157a3585
> @!g:real; {a glue ratio, as a floating point number}
3159,3174c3587,3604
< begin if p>null then print("[]");
< {indicate that there's been some truncation}
< return;
< end;
< n←0;
< while p>null do
< begin print_ln; print_current_string; {display the nesting history}
< if p>mem_end then {pointer out of range}
< begin print("Bad link, display aborted."); return;
< end;
< incr(n); if n>breadth_max then {time to stop}
< begin print("etc."); return;
< end;
< @<Display node |p|@>;
< p←link(p);
< end;
---
> begin if p>null then print(" []");
> {indicate that there's been some truncation}
> return;
> end;
> n:=0;
> while p>mem_min do
> begin print_ln; print_current_string; {display the nesting history}
> if p>mem_end then {pointer out of range}
> begin print("Bad link, display aborted."); return;
> @.Bad link...@>
> end;
> incr(n); if n>breadth_max then {time to stop}
> begin print("etc."); return;
> @.etc@>
> end;
> @<Display node |p|@>;
> p:=link(p);
> end;
3180,3195c3610,3625
< else case type(p) of
< hlist_node,vlist_node,unset_node: @<Display box |p|@>;
< rule_node: @<Display rule |p|@>;
< ins_node: @<Display insertion |p|@>;
< whatsit_node: @<Display the whatsit node |p|@>;
< glue_node: @<Display glue |p|@>;
< kern_node: @<Display kern |p|@>;
< math_node: @<Display math node |p|@>;
< ligature_node: @<Display ligature |p|@>;
< penalty_node: @<Display penalty |p|@>;
< disc_node: @<Display discretionary |p|@>;
< mark_node: @<Display mark |p|@>;
< adjust_node: @<Display adjustment |p|@>;
< @t\4@>@<Cases of |show_node_list| that arise in mlists only@>@;
< othercases print("Unknown node type!")
< endcases
---
> else case type(p) of
> hlist_node,vlist_node,unset_node: @<Display box |p|@>;
> rule_node: @<Display rule |p|@>;
> ins_node: @<Display insertion |p|@>;
> whatsit_node: @<Display the whatsit node |p|@>;
> glue_node: @<Display glue |p|@>;
> kern_node: @<Display kern |p|@>;
> math_node: @<Display math node |p|@>;
> ligature_node: @<Display ligature |p|@>;
> penalty_node: @<Display penalty |p|@>;
> disc_node: @<Display discretionary |p|@>;
> mark_node: @<Display mark |p|@>;
> adjust_node: @<Display adjustment |p|@>;
> @t\4@>@<Cases of |show_node_list| that arise in mlists only@>@;
> othercases print("Unknown node type!")
> endcases
3204,3209c3634,3639
< @<Display special fields of the unset node |p|@>
< else begin @<Display the value of |glue_set(p)|@>;
< if shift_amount(p)≠0 then
< begin print(", shifted "); print_scaled(shift_amount(p));
< end;
< end;
---
> @<Display special fields of the unset node |p|@>
> else begin @<Display the value of |glue_set(p)|@>;
> if shift_amount(p)<>0 then
> begin print(", shifted "); print_scaled(shift_amount(p));
> end;
> end;
3214,3219c3644,3653
< begin if span_count(p)≠min_quarterword then
< begin print(" ("); print_int(qo(span_count(p))+1);
< print(" columns)");
< end;
< print(", stretch "); print_glue(glue_stretch(p),glue_order(p),0);
< print(", shrink "); print_glue(glue_shrink(p),glue_sign(p),0);
---
> begin if span_count(p)<>min_quarterword then
> begin print(" ("); print_int(qo(span_count(p))+1);
> print(" columns)");
> end;
> if glue_stretch(p)<>0 then
> begin print(", stretch "); print_glue(glue_stretch(p),glue_order(p),0);
> end;
> if glue_shrink(p)<>0 then
> begin print(", shrink "); print_glue(glue_shrink(p),glue_sign(p),0);
> end;
3223c3657,3664
< a structured type instead of an ordinary |real|.
---
> a structured type instead of an ordinary |real|. Note that this routine
> should avoid arithmetic errors even if the |glue_set| field holds an
> arbitrary random value. The following code assumes that a properly
> formed nonzero |real| number has absolute value $2^{20}$ or more when
> it is regarded as an integer; this precaution was adequate to prevent
> floating point underflow on the author's computer.
> @^system dependencies@>
> @^dirty \PASCAL@>
3226,3235c3667,3679
< if glue_set(p)≠0 then
< begin print(", glue set ");
< if glue_sign(p)=shrinking then print("- ");
< if abs(glue_set(p))>20000.0 then
< begin if glue_set(p)>0 then print_char(">")
< else print("< -");
< print_glue(20000*unity,glue_order(p),0);
< end
< else print_glue(round(glue_set(p)*unity),glue_order(p),0);
< end
---
> g:=float(glue_set(p));
> if (g<>float_constant(0))and(glue_sign(p)<>normal) then
> begin print(", glue set ");
> if glue_sign(p)=shrinking then print("- ");
> if abs(mem[p+glue_offset].int)<@'4000000 then print("?.?")
> else if abs(g)>float_constant(20000) then
> begin if g>float_constant(0) then print_char(">")
> else print("< -");
> print_glue(20000*unity,glue_order(p),0);
> end
> else print_glue(round(unity*g),glue_order(p),0);
> @^real multiplication@>
> end
3244c3688,3691
< print(", natural size "); print_scaled(width(p));
---
> print(", natural size "); print_scaled(height(p));
> print("; split("); print_spec(split_top_ptr(p),0);
> print_char(","); print_scaled(depth(p));
> print("); float cost "); print_int(float_cost(p));
3249,3264c3696,3711
< if subtype(p)≥a_leaders then @<Display leaders |p|@>
< else begin print_esc("glue");
< if subtype(p)≠normal then
< begin print_char("(");
< if subtype(p)<cond_math_glue then
< print_skip_param(subtype(p)-1)
< else if subtype(p)=cond_math_glue then print_esc("non_script")
< else print_esc("mskip");
< print_char(")");
< end;
< if subtype(p)≠cond_math_glue then
< begin print_char(" ");
< if subtype(p)<cond_math_glue then print_spec(glue_ptr(p),0)
< else print_spec(glue_ptr(p),"mu");
< end;
< end
---
> if subtype(p)>=a_leaders then @<Display leaders |p|@>
> else begin print_esc("glue");
> if subtype(p)<>normal then
> begin print_char("(");
> if subtype(p)<cond_math_glue then
> print_skip_param(subtype(p)-1)
> else if subtype(p)=cond_math_glue then print_esc("nonscript")
> else print_esc("mskip");
> print_char(")");
> end;
> if subtype(p)<>cond_math_glue then
> begin print_char(" ");
> if subtype(p)<cond_math_glue then print_spec(glue_ptr(p),0)
> else print_spec(glue_ptr(p),"mu");
> end;
> end
3267c3714
< begin print_char("\");
---
> begin print_esc("");
3274,3279c3721,3732
< @ @<Display kern |p|@>=
< if subtype(p)=normal then
< begin print_esc("kern"); print_scaled(width(p));
< end
< else begin print_esc("mkern"); print_scaled(width(p)); print("mu");
< end
---
> @ An ``explicit'' kern value is indicated implicitly by an explicit space.
>
> @<Display kern |p|@>=
> if subtype(p)<>mu_glue then
> begin print_esc("kern");
> if subtype(p)<>normal then print_char(" ");
> print_scaled(width(p));
> if subtype(p)=acc_kern then print(" (for accent)");
> @.for accent@>
> end
> else begin print_esc("mkern"); print_scaled(width(p)); print("mu");
> end
3285,3287c3738,3740
< if width(p)≠0 then
< begin print(", surrounded "); print_scaled(width(p));
< end;
---
> if width(p)<>0 then
> begin print(", surrounded "); print_scaled(width(p));
> end;
3292,3293c3745,3748
< font_in_short_display←font(lig_char(p));
< short_display(lig_ptr(p)); print_char(")");
---
> if subtype(p)>1 then print_char("|");
> font_in_short_display:=font(lig_char(p)); short_display(lig_ptr(p));
> if odd(subtype(p)) then print_char("|");
> print_char(")");
3306,3309c3761,3763
< begin print(" replacing "); print_int(replace_count(p));
< end;
< if pre_break(p)=null then print(" (exhyphen)")
< else node_list_display(pre_break(p)); {recursive call}
---
> begin print(" replacing "); print_int(replace_count(p));
> end;
> node_list_display(pre_break(p)); {recursive call}
3325,3330c3779,3784
< begin @<Assign the values |depth_threshold←show_box_depth| and
< |breadth_max←show_box_breadth|@>;
< if breadth_max≤0 then breadth_max←5;
< if pool_ptr+depth_threshold≥pool_size then
< depth_threshold←pool_size-pool_ptr-1;
< {now there's enough room for prefix string}
---
> begin @<Assign the values |depth_threshold:=show_box_depth| and
> |breadth_max:=show_box_breadth|@>;
> if breadth_max<=0 then breadth_max:=5;
> if pool_ptr+depth_threshold>=pool_size then
> depth_threshold:=pool_size-pool_ptr-1;
> {now there's enough room for prefix string}
3333a3788
>
3349c3804
< of a token list that is losing one reference}
---
> of a token list that is losing one reference}
3356a3812,3815
> @d fast_delete_glue_ref(#)==@t@>@;@/
> begin if glue_ref_count(#)=null then free_node(#,glue_spec_size)
> else decr(glue_ref_count(#));
> end
3359,3361c3818
< begin if glue_ref_count(p)=null then free_node(p,glue_spec_size)
< else decr(glue_ref_count(p));
< end;
---
> fast_delete_glue_ref(p);
3363a3821,3822
> In practice, the nodes deleted are usually charnodes (about 2/3 of the time),
> and they are glue nodes in about half of the remaining cases.
3369,3398c3828,3860
< begin while p≠null do
< begin q←link(p);
< if is_char_node(p) then free_avail(p)
< else begin case type(p) of
< hlist_node,vlist_node,unset_node: begin flush_node_list(list_ptr(p));
< free_node(p,box_node_size); goto done;
< end;
< rule_node: begin free_node(p,rule_node_size); goto done;
< end;
< ins_node: begin flush_node_list(ins_ptr(p));
< free_node(p,ins_node_size); goto done;
< end;
< whatsit_node: @<Wipe out the whatsit node |p| and |goto done|@>;
< glue_node: begin delete_glue_ref(glue_ptr(p));
< flush_node_list(leader_ptr(p));
< end;
< kern_node,math_node,penalty_node: do_nothing;
< ligature_node: flush_node_list(lig_ptr(p));
< mark_node: delete_token_ref(mark_ptr(p));
< disc_node: begin flush_node_list(pre_break(p));
< flush_node_list(post_break(p));
< end;
< adjust_node: flush_node_list(adjust_ptr(p));
< othercases confusion("flushing")
< @:confusion flushing}{\quad flushing@>
< endcases;@/
< free_node(p,small_node_size);
< done:end;
< p←q;
< end;
---
> begin while p<>null do
> @^inner loop@>
> begin q:=link(p);
> if is_char_node(p) then free_avail(p)
> else begin case type(p) of
> hlist_node,vlist_node,unset_node: begin flush_node_list(list_ptr(p));
> free_node(p,box_node_size); goto done;
> end;
> rule_node: begin free_node(p,rule_node_size); goto done;
> end;
> ins_node: begin flush_node_list(ins_ptr(p));
> delete_glue_ref(split_top_ptr(p));
> free_node(p,ins_node_size); goto done;
> end;
> whatsit_node: @<Wipe out the whatsit node |p| and |goto done|@>;
> glue_node: begin fast_delete_glue_ref(glue_ptr(p));
> if leader_ptr(p)<>null then flush_node_list(leader_ptr(p));
> end;
> kern_node,math_node,penalty_node: do_nothing;
> ligature_node: flush_node_list(lig_ptr(p));
> mark_node: delete_token_ref(mark_ptr(p));
> disc_node: begin flush_node_list(pre_break(p));
> flush_node_list(post_break(p));
> end;
> adjust_node: flush_node_list(adjust_ptr(p));
> @t\4@>@<Cases of |flush_node_list| that arise in mlists only@>@;
> othercases confusion("flushing")
> @:this can't happen flushing}{\quad flushing@>
> endcases;@/
> free_node(p,small_node_size);
> done:end;
> p:=q;
> end;
3399a3862
>
3401,3406c3864,3868
< Another recursive operation on boxes is sometimes needed: The procedure
< @^recursion@>
< |copy_node_list| returns a pointer to another node list that has the
< same structure and meaning as the original. Note that since glue
< specifications and token lists have reference counts, we need not
< make copies of them. Reference counts can never get too large to fit in a
---
> Another recursive operation that acts on boxes is sometimes needed: The
> procedure |copy_node_list| returns a pointer to another node list that has
> the same structure and meaning as the original. Note that since glue
> specifications and token lists have reference counts, we need not make
> copies of them. Reference counts can never get too large to fit in a
3408a3871
> @^recursion@>
3421a3885
> @^data structure assumptions@>
3424c3888
< node list that starts at |p| and returns a pointer to the new list}
---
> node list that starts at |p| and returns a pointer to the new list}
3429,3435c3893,3899
< begin h←get_avail; q←h;
< while p≠null do
< begin @<Make a copy of node |p| in node |r|@>;
< link(q)←r; q←r; p←link(p);
< end;
< link(q)←null; q←link(h); free_avail(h);
< copy_node_list←q;
---
> begin h:=get_avail; q:=h;
> while p<>null do
> begin @<Make a copy of node |p| in node |r|@>;
> link(q):=r; q:=r; p:=link(p);
> end;
> link(q):=null; q:=link(h); free_avail(h);
> copy_node_list:=q;
3439,3440c3903,3904
< words←1; {this setting occurs in more branches than any other}
< if is_char_node(p) then r←get_avail
---
> words:=1; {this setting occurs in more branches than any other}
> if is_char_node(p) then r:=get_avail
3442c3906
< of initial words not yet copied@>;
---
> of initial words not yet copied@>;
3444,3445c3908,3909
< begin decr(words); mem[r+words]←mem[p+words];
< end
---
> begin decr(words); mem[r+words]:=mem[p+words];
> end
3449,3458c3913,3924
< hlist_node,vlist_node,unset_node: begin r←get_node(box_node_size);
< mem[r+6]←mem[p+6]; mem[r+5]←mem[p+5]; {copy the last two words}
< list_ptr(r)←copy_node_list(list_ptr(p)); {this affects |mem[r+5]|}
< words←5;
< end;
< rule_node: begin r←get_node(rule_node_size); words←rule_node_size;
< end;
< ins_node: begin r←get_node(ins_node_size);
< ins_ptr(r)←copy_node_list(ins_ptr(p)); words←ins_node_size-1;
< end;
---
> hlist_node,vlist_node,unset_node: begin r:=get_node(box_node_size);
> mem[r+6]:=mem[p+6]; mem[r+5]:=mem[p+5]; {copy the last two words}
> list_ptr(r):=copy_node_list(list_ptr(p)); {this affects |mem[r+5]|}
> words:=5;
> end;
> rule_node: begin r:=get_node(rule_node_size); words:=rule_node_size;
> end;
> ins_node: begin r:=get_node(ins_node_size); mem[r+4]:=mem[p+4];
> add_glue_ref(split_top_ptr(p));
> ins_ptr(r):=copy_node_list(ins_ptr(p)); {this affects |mem[r+4]|}
> words:=ins_node_size-1;
> end;
3460,3480c3926,3946
< point to it; set |words| to the number of initial words not yet copied@>;
< glue_node: begin r←get_node(small_node_size); add_glue_ref(glue_ptr(p));
< glue_ptr(r)←glue_ptr(p); leader_ptr(r)←copy_node_list(leader_ptr(p));
< end;
< kern_node,math_node,penalty_node: begin r←get_node(small_node_size);
< words←small_node_size;
< end;
< ligature_node: begin r←get_node(small_node_size);
< mem[lig_char(r)]←mem[lig_char(p)]; {copy |font| and |character|}
< lig_ptr(r)←copy_node_list(lig_ptr(p));
< end;
< disc_node: begin r←get_node(small_node_size);
< pre_break(r)←copy_node_list(pre_break(p));
< post_break(r)←copy_node_list(post_break(p));
< end;
< mark_node: begin r←get_node(small_node_size); add_token_ref(mark_ptr(p));
< words←small_node_size;
< end;
< adjust_node: begin r←get_node(small_node_size);
< adjust_ptr(r)←copy_node_list(adjust_ptr(p));
< end;
---
> point to it; set |words| to the number of initial words not yet copied@>;
> glue_node: begin r:=get_node(small_node_size); add_glue_ref(glue_ptr(p));
> glue_ptr(r):=glue_ptr(p); leader_ptr(r):=copy_node_list(leader_ptr(p));
> end;
> kern_node,math_node,penalty_node: begin r:=get_node(small_node_size);
> words:=small_node_size;
> end;
> ligature_node: begin r:=get_node(small_node_size);
> mem[lig_char(r)]:=mem[lig_char(p)]; {copy |font| and |character|}
> lig_ptr(r):=copy_node_list(lig_ptr(p));
> end;
> disc_node: begin r:=get_node(small_node_size);
> pre_break(r):=copy_node_list(pre_break(p));
> post_break(r):=copy_node_list(post_break(p));
> end;
> mark_node: begin r:=get_node(small_node_size); add_token_ref(mark_ptr(p));
> words:=small_node_size;
> end;
> adjust_node: begin r:=get_node(small_node_size);
> adjust_ptr(r):=copy_node_list(adjust_ptr(p));
> end; {|words=1=small_node_size-1|}
3482c3948
< @:confusion copying}{\quad copying@>
---
> @:this can't happen copying}{\quad copying@>
3483a3950
>
3489,3490c3956,3957
< e.g., `\.{\\chcode \`\\\${} = 3}' to make \.{\char'44} a math delimiter,
< and the command code |math_delim| is equal to@@3. Some other codes have
---
> e.g., `\.{\\catcode \`\\\${} = 3}' to make \.{\char'44} a math delimiter,
> and the command code |math_shift| is equal to~3. Some other codes have
3496,3497c3963,3964
< ``chcode'' commands, several of which are used also as ordinary commands
< when the chcode cannot emerge from \TeX's scanning routine.
---
> ``catcode'' commands, several of which share their numeric codes with
> ordinary commands when the catcode cannot emerge from \TeX's scanning routine.
3499,3505c3966,3973
< @d escape=0 {escape delimiter (called \.{\\} in the \TeX\ manual)}
< @d relax=0 { do nothing ( \.{\\relax} )}
< @d left_brace=1 {beginning of a group ( \.{\{} )}
< @d right_brace=2 {ending of a group ( \.{\}} )}
< @d math_delim=3 {mathematics delimiter ( \.{\$} )}
< @d tab_mark=4 {alignment delimiter ( \.{\&}, \.{\\span} )}
< @d car_ret=5 {carriage return ( |carriage_return|, also \.{\\cr} )}
---
> @d escape=0 {escape delimiter (called \.\\ in {\sl The \TeX book\/})}
> @:TeXbook}{\sl The \TeX book@>
> @d relax=0 {do nothing ( \.{\\relax} )}
> @d left_brace=1 {beginning of a group ( \.\{ )}
> @d right_brace=2 {ending of a group ( \.\} )}
> @d math_shift=3 {mathematics shift character ( \.\$ )}
> @d tab_mark=4 {alignment delimiter ( \.\&, \.{\\span} )}
> @d car_ret=5 {end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} )}
3507c3975
< @d mac_param=6 {macro parameter symbol ( \.{\#} )}
---
> @d mac_param=6 {macro parameter symbol ( \.\# )}
3509,3511c3977,3979
< @d sub_mark=8 {subscript ( \.{\char'176} )}
< @d ignore=9 {characters to ignore ( \.{\^\^J} )}
< @d endv=9 {end of \<v↓j> list in alignment template}
---
> @d sub_mark=8 {subscript ( \.{\char'137} )}
> @d ignore=9 {characters to ignore ( \.{\^\^@@} )}
> @d endv=9 {end of \<v_j> list in alignment template}
3515c3983
< @d active_char=13 {characters that invoke macros ( \.{\^\^[} )}
---
> @d active_char=13 {characters that invoke macros ( \.{\char`\~} )}
3518c3986
< @d end_line=14 {characters that introduce comments ( \.{\char'45} )}
---
> @d comment=14 {characters that introduce comments ( \.\% )}
3520c3988
< @d stop=14 {end of input ( \.{\\end}, \.{\\dump} )}
---
> @d stop=14 {end of job ( \.{\\end}, \.{\\dump} )}
3523c3991
< @d max_char_code=15 {largest chcode for individual characters}
---
> @d max_char_code=15 {largest catcode for individual characters}
3525c3993,3995
< @ Next are the ordinary run-of-the-mill command codes.
---
> @ Next are the ordinary run-of-the-mill command codes. Codes that are
> |min_internal| or more represent internal quantities that might be
> expanded by `\.{\\the}'.
3530,3538c4000,4009
< @d input=19 {input a source file ( \.{\\input} )}
< @d xray=20 {peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.@@)}
< @d make_box=21 {make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.@@)}
< @d hmove=22 {horizontal motion ( \.{\\moveleft}, \.{\\moveright} )}
< @d vmove=23 {vertical motion ( \.{\\raise}, \.{\\lower} )}
< @d unbox=24 {unglue a box ( \.{\\unbox} )}
< @d unskip=25 {nullify glue ( \.{\\unskip} )}
< @d hskip=26 {horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.@@)}
< @d vskip=27 {vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.@@)}
---
> @d xray=19 {peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~)}
> @d make_box=20 {make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~)}
> @d hmove=21 {horizontal motion ( \.{\\moveleft}, \.{\\moveright} )}
> @d vmove=22 {vertical motion ( \.{\\raise}, \.{\\lower} )}
> @d un_hbox=23 {unglue a box ( \.{\\unhbox}, \.{\\unhcopy} )}
> @d un_vbox=24 {unglue a box ( \.{\\unvbox}, \.{\\unvcopy} )}
> @d remove_item=25 {nullify last item ( \.{\\unpenalty},
> \.{\\unkern}, \.{\\unskip} )}
> @d hskip=26 {horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~)}
> @d vskip=27 {vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~)}
3540c4011
< @d kern=29 {fixed space ( \.{kern})}
---
> @d kern=29 {fixed space ( \.{\\kern})}
3542c4013
< @d leader_ship=31 {use a box ( \.{\\shipout}, \.{\\leaders}, etc.@@)}
---
> @d leader_ship=31 {use a box ( \.{\\shipout}, \.{\\leaders}, etc.~)}
3550,3568c4021,4039
< @d ignore_space=39 {gobble |spacer| tokens ( \.{\\ignorespace} )}
< @d break_penalty=40 {additional badness ( \.{\\penalty}, \.{\\dpenalty}@@)}
< @d start_par=41 {begin paragraph ( \.{\\indent}, \.{\\noindent} )}
< @d ital_corr=42 {italic correction ( \.{\\/} )}
< @d accent=43 {attach accent in text ( \.{\\accent} )}
< @d math_accent=44 {attach accent in math ( \.{\\mathaccent} )}
< @d discretionary=45 {discretionary texts ( \.{\\discretionary} )}
< @d eq_no=46 {equation number ( \.{\\eqno}, \.{\\leqno} )}
< @d left_right=47 {variable delimiter ( \.{\\left}, \.{\\right} )}
< @d math_comp=48 {component of formula ( \.{\\mathbin}, etc.@@)}
< @d limit_switch=49 {diddle limit conventions ( \.{\\limitswitch}@@)}
< @d above=50 {generalized fraction ( \.{\\above}, \.{\\atop}, etc.@@)}
< @d math_style=51 {style specification ( \.{\\displaystyle}, etc.@@)}
< @d non_script=52 {conditional math glue ( \.{\\nonscript} )}
< @d vcenter=53 {vertically center a vbox ( \.{\\vcenter} )}
< @d case_shift=54 {force specific case ( \.{\\lowercase}, \.{\\uppercase}@@)}
< @d if_test=55 {select conditional text ( \.{\\if}, \.{\\ifeven}, etc.@@)}
< @d case_branch=56 {choose numbered text ( \.{\\case} )}
< @d else_code=57 {delimiter for conditionals ( \.{\\else} )}
---
> @d ignore_spaces=39 {gobble |spacer| tokens ( \.{\\ignorespaces} )}
> @d after_assignment=40 {save till assignment is done ( \.{\\afterassignment} )}
> @d after_group=41 {save till group is done ( \.{\\aftergroup} )}
> @d break_penalty=42 {additional badness ( \.{\\penalty} )}
> @d start_par=43 {begin paragraph ( \.{\\indent}, \.{\\noindent} )}
> @d ital_corr=44 {italic correction ( \.{\\/} )}
> @d accent=45 {attach accent in text ( \.{\\accent} )}
> @d math_accent=46 {attach accent in math ( \.{\\mathaccent} )}
> @d discretionary=47 {discretionary texts ( \.{\\-}, \.{\\discretionary} )}
> @d eq_no=48 {equation number ( \.{\\eqno}, \.{\\leqno} )}
> @d left_right=49 {variable delimiter ( \.{\\left}, \.{\\right} )}
> @d math_comp=50 {component of formula ( \.{\\mathbin}, etc.~)}
> @d limit_switch=51 {diddle limit conventions ( \.{\\displaylimits}, etc.~)}
> @d above=52 {generalized fraction ( \.{\\above}, \.{\\atop}, etc.~)}
> @d math_style=53 {style specification ( \.{\\displaystyle}, etc.~)}
> @d math_choice=54 {choice specification ( \.{\\mathchoice} )}
> @d non_script=55 {conditional math glue ( \.{\\nonscript} )}
> @d vcenter=56 {vertically center a vbox ( \.{\\vcenter} )}
> @d case_shift=57 {force specific case ( \.{\\lowercase}, \.{\\uppercase}~)}
3570,3581c4041,4055
< @d extension=59 {extensions to \TeX\ ( \.{\\send}, \.{\\open}, etc.@@)}
< @d group_begin=60 {begin local grouping ( \.{\\groupbegin} )}
< @d group_end=61 {end local grouping ( \.{\\groupend} )}
< @d omit=62 {omit alignment template ( \.{\\omit} )}
< @d ex_space=63 {explicit space ( \.{\\\ } )}
< @d radical=64 {square root and similar signs ( \.{\\radical} )}
< @d the=66 {convert to arabic or roman numerals ( \.{\\number} )}
< @d number=65 {read a parameter or register ( \.{\\the}, \.{\\minus} )}
< @d register=67 {read a register ( \.{\\count}, \.{\\dimen}, \.{\\skip} )}
< @d last_skip=68 {most recent glue ( \.{\\lastskip} )}
< @d set_aux=69 {specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )}
< @d max_non_prefixed_command=69 {largest command code that can't be \.{\\global}}
---
> @d extension=59 {extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~)}
> @d in_stream=60 {files for reading ( \.{\\openin}, \.{\\closein} )}
> @d begin_group=61 {begin local grouping ( \.{\\begingroup} )}
> @d end_group=62 {end local grouping ( \.{\\endgroup} )}
> @d omit=63 {omit alignment template ( \.{\\omit} )}
> @d ex_space=64 {explicit space ( \.{\\\ } )}
> @d no_boundary=65 {suppress boundary ligatures ( \.{\\noboundary} )}
> @d radical=66 {square root and similar signs ( \.{\\radical} )}
> @d end_cs_name=67 {end control sequence ( \.{\\endcsname} )}
> @d min_internal=68 {the smallest code that can follow \.{\\the}}
> @d char_given=68 {character code defined by \.{\\chardef}}
> @d math_given=69 {math code defined by \.{\\mathchardef}}
> @d last_item=70 {most recent item ( \.{\\lastpenalty},
> \.{\\lastkern}, \.{\\lastskip} )}
> @d max_non_prefixed_command=70 {largest command code that can't be \.{\\global}}
3584a4059,4060
> Codes that are |max_internal| or less represent internal quantities
> that might be expanded by `\.{\\the}'.
3586,3612c4062,4095
< @d assign_toks=70 {special token list ( \.{\\output}, \.{\\everypar} )}
< @d assign_int=71 {user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.@@)}
< @d assign_dimen=72 {user-defined length ( \.{\\hsize}, etc.@@)}
< @d assign_glue=73 {user-defined glue ( \.{\\baselineskip}, etc.@@)}
< @d assign_tex_info=74 {user-defined font parameter ( \.{\\texinfo} )}
< @d hang_indent=75 {specify hanging indentation ( \.{\\hangindent} )}
< @d def_code=76 {define a character code ( \.{\\chcode}, etc.@@)}
< @d def_family=77 {declare math fonts ( \.{\\textfont}, etc.@@)}
< @d set_font=78 {set current font ( \.{\\:} )}
< @d set_family=79 {set current family ( \.{\\fam} )}
< @d prefix=80 {qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} )}
< @d let=81 {alternative name for a control sequence ( \.{\\let} )}
< @d def=82 {macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )}
< @d def_font=83 {define a font file ( \.{\\font} )}
< @d set_register=84 {set a register ( \.{\\setcount}, \.{\\setdimen},
< \.{\\setskip} )}
< @d adv_register=85 {advance a register ( \.{\\advcount}, \.{\\advdimen},
< \.{\\advskip} )}
< @d mult_register=86 {multiply a register ( \.{\\multcount}, \.{\\multdimen},
< \.{\\multskip} )}
< @d div_register=87 {divide a register ( \.{\\divcount}, \.{\\divdimen},
< \.{\\divskip} )}
< @d set_box=88 {set a box ( \.{\\setbox} )}
< @d set_shape=89 {specify fancy paragraph shape ( \.{\\parshape} )}
< @d hyph_data=90 {hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )}
< @d set_interaction=91 {define level of interaction ( \.{\\batchmode}, etc.@@)}
< @d max_command=91 {the largest command code seen at |big_switch|}
---
> @d toks_register=71 {token list register ( \.{\\toks} )}
> @d assign_toks=72 {special token list ( \.{\\output}, \.{\\everypar}, etc.~)}
> @d assign_int=73 {user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~)}
> @d assign_dimen=74 {user-defined length ( \.{\\hsize}, etc.~)}
> @d assign_glue=75 {user-defined glue ( \.{\\baselineskip}, etc.~)}
> @d assign_mu_glue=76 {user-defined muglue ( \.{\\thinmuskip}, etc.~)}
> @d assign_font_dimen=77 {user-defined font dimension ( \.{\\fontdimen} )}
> @d assign_font_int=78 {user-defined font integer ( \.{\\hyphenchar},
> \.{\\skewchar} )}
> @d set_aux=79 {specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )}
> @d set_prev_graf=80 {specify state info ( \.{\\prevgraf} )}
> @d set_page_dimen=81 {specify state info ( \.{\\pagegoal}, etc.~)}
> @d set_page_int=82 {specify state info ( \.{\\deadcycles},
> \.{\\insertpenalties} )}
> @d set_box_dimen=83 {change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} )}
> @d set_shape=84 {specify fancy paragraph shape ( \.{\\parshape} )}
> @d def_code=85 {define a character code ( \.{\\catcode}, etc.~)}
> @d def_family=86 {declare math fonts ( \.{\\textfont}, etc.~)}
> @d set_font=87 {set current font ( font identifiers )}
> @d def_font=88 {define a font file ( \.{\\font} )}
> @d register=89 {internal register ( \.{\\count}, \.{\\dimen}, etc.~)}
> @d max_internal=89 {the largest code that can follow \.{\\the}}
> @d advance=90 {advance a register or parameter ( \.{\\advance} )}
> @d multiply=91 {multiply a register or parameter ( \.{\\multiply} )}
> @d divide=92 {divide a register or parameter ( \.{\\divide} )}
> @d prefix=93 {qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} )}
> @d let=94 {assign a command code ( \.{\\let}, \.{\\futurelet} )}
> @d shorthand_def=95 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)}
> @d read_to_cs=96 {read into a control sequence ( \.{\\read} )}
> @d def=97 {macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )}
> @d set_box=98 {set a box ( \.{\\setbox} )}
> @d hyph_data=99 {hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )}
> @d set_interaction=100 {define level of interaction ( \.{\\batchmode}, etc.~)}
> @d max_command=100 {the largest command code seen at |big_switch|}
3616c4099,4100
< than |max_command| so that their special nature is easily discernable.
---
> than |max_command| so that their special nature is easily discernible.
> The ``expandable'' commands come first.
3619,3627c4103,4122
< @d top_bot_mark=max_command+2 {inserted mark ( \.{\\topmark}, etc.\ }
< @d call=max_command+3 {non-long, non-outer control sequence}
< @d long_call=max_command+4 {long, non-outer control sequence}
< @d outer_call=max_command+5 {non-long, outer control sequence}
< @d long_outer_call=max_command+6 {long, outer control sequence}
< @d glue_ref=max_command+7 {pointer to a glue specification}
< @d shape_ref=max_command+8 {pointer to a parshape specification}
< @d box_ref=max_command+9 {pointer to a box node, or |null|}
< @d data=max_command+10 {the equivalent is simply a halfword number}
---
> @d expand_after=max_command+2 {special expansion ( \.{\\expandafter} )}
> @d no_expand=max_command+3 {special nonexpansion ( \.{\\noexpand} )}
> @d input=max_command+4 {input a source file ( \.{\\input}, \.{\\endinput} )}
> @d if_test=max_command+5 {conditional text ( \.{\\if}, \.{\\ifcase}, etc.~)}
> @d fi_or_else=max_command+6 {delimiters for conditionals ( \.{\\else}, etc.~)}
> @d cs_name=max_command+7 {make a control sequence from tokens ( \.{\\csname} )}
> @d convert=max_command+8 {convert to text ( \.{\\number}, \.{\\string}, etc.~)}
> @d the=max_command+9 {expand an internal quantity ( \.{\\the} )}
> @d top_bot_mark=max_command+10 {inserted mark ( \.{\\topmark}, etc.~)}
> @d call=max_command+11 {non-long, non-outer control sequence}
> @d long_call=max_command+12 {long, non-outer control sequence}
> @d outer_call=max_command+13 {non-long, outer control sequence}
> @d long_outer_call=max_command+14 {long, outer control sequence}
> @d end_template=max_command+15 {end of an alignment template}
> @d dont_expand=max_command+16 {the following token was marked by \.{\\noexpand}}
> @d glue_ref=max_command+17 {the equivalent points to a glue specification}
> @d shape_ref=max_command+18 {the equivalent points to a parshape specification}
> @d box_ref=max_command+19 {the equivalent points to a box node, or is |null|}
> @d data=max_command+20 {the equivalent is simply a halfword number}
>
3654,3655c4149,4150
< \yskip\noindent The mode is temporarily set to zero while processing \.{\\send}
< texts in the |ship_out| routine.
---
> \yskip\noindent The mode is temporarily set to zero while processing \.{\\write}
> texts in the |ship_out| routine.
3666c4161
< @p procedure print_mode(@!m:integer); {prints the current mode}
---
> @p procedure print_mode(@!m:integer); {prints the mode represented by |m|}
3668,3677c4163,4173
< case m div (max_command+1) of
< 0:print("vertical");
< 1:print("horizontal");
< 2:print("displayed math");
< end
< else case (-m) div (max_command+1) of
< 0:print("internal vertical");
< 1:print("restricted horizontal");
< 2:print("math");
< end;
---
> case m div (max_command+1) of
> 0:print("vertical");
> 1:print("horizontal");
> 2:print("display math");
> end
> else if m=0 then print("no")
> else case (-m) div (max_command+1) of
> 0:print("internal vertical");
> 1:print("restricted horizontal");
> 2:print("math");
> end;
3694c4190
< \yskip\hang|already| is the number of lines of the current paragraph that
---
> \yskip\hang|prev_graf| is the number of lines of the current paragraph that
3697c4193
< \yskip\hang|aux| is an auxiliary integer that gives further information
---
> \yskip\hang|aux| is an auxiliary |memory_word| that gives further information
3703c4199
< calculations, or it is |≤1000|pt if the next box on the vertical list is to
---
> calculations, or it is |<=-1000|pt if the next box on the vertical list is to
3705,3706c4201,4204
< known as |space_factor|; it holds the current space factor use in spacing
< calculations. In math mode, |aux| is also known as |incompleat_noad|; if
---
> known as |space_factor| and |clang|; it holds the current space factor used in
> spacing calculations, and the current language used for hyphenation.
> (The value of |clang| is undefined in restricted horizontal mode.)
> In math mode, |aux| is also known as |incompleat_noad|; if
3711c4209
< There is also a sixth quantity, |mode_line|, which is used to correlate
---
> There is also a sixth quantity, |mode_line|, which correlates
3714c4212
< of this line number is used as the |mode_line| at the level of the
---
> of this line number is the |mode_line| at the level of the
3716a4215,4216
> In horizontal mode, the |prev_graf| field is used for initial language data.
>
3718c4218
< |tail|, |already|, |aux|, and |mode_line| values for all semantic levels
---
> |tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels
3720c4220
< level is kept in the global quantities |mode|, |head|, |tail|, |already|,
---
> level is kept in the global quantities |mode|, |head|, |tail|, |prev_graf|,
3727,3730c4227,4231
< @!list_state_record=record@!mode_field:-mmode..mmode;
< @!head_field,@!tail_field: pointer;
< @!already_field,@!aux_field,@!ml_field: integer;
< end;
---
> @!list_state_record=record@!mode_field:-mmode..mmode;@+
> @!head_field,@!tail_field: pointer;
> @!pg_field,@!ml_field: integer;@+
> @!aux_field: memory_word;
> end;
3735c4236
< @d already==cur_list.already_field {number of paragraph lines accumulated}
---
> @d prev_graf==cur_list.pg_field {number of paragraph lines accumulated}
3737,3739c4238,4241
< @d prev_depth==aux {the name of |aux| in vertical mode}
< @d space_factor==aux {the name of |aux| in horizontal mode}
< @d incompleat_noad==aux {the name of |aux| in math mode}
---
> @d prev_depth==aux.sc {the name of |aux| in vertical mode}
> @d space_factor==aux.hh.lh {part of |aux| in horizontal mode}
> @d clang==aux.hh.rh {the other part of |aux| in horizontal mode}
> @d incompleat_noad==aux.int {the name of |aux| in math mode}
3742d4243
<
3752,3753c4253,4254
< @d tail_append(#)==begin link(tail)←#; tail←link(tail);
< end
---
> @d tail_append(#)==begin link(tail):=#; tail:=link(tail);
> end
3764,3766c4265,4268
< nest_ptr←0; max_nest_stack←0;
< mode←vmode; head←contrib_head; tail←contrib_head;
< prev_depth←ignore_depth; mode_line←0; already←0; shown_mode←0;@/
---
> nest_ptr:=0; max_nest_stack:=0;
> mode:=vmode; head:=contrib_head; tail:=contrib_head;
> prev_depth:=ignore_depth; mode_line:=0;
> prev_graf:=0; shown_mode:=0;
3775,3779c4277,4282
< begin max_nest_stack←nest_ptr;
< if nest_ptr=nest_size then overflow("semantic nest size",nest_size);
< end;
< nest[nest_ptr]←cur_list; {stack the record}
< incr(nest_ptr); head←get_avail; tail←head; already←0; mode_line←line;
---
> begin max_nest_stack:=nest_ptr;
> if nest_ptr=nest_size then overflow("semantic nest size",nest_size);
> @:TeX capacity exceeded semantic nest size}{\quad semantic nest size@>
> end;
> nest[nest_ptr]:=cur_list; {stack the record}
> incr(nest_ptr); head:=get_avail; tail:=head; prev_graf:=0; mode_line:=line;
3788c4291
< begin free_avail(head); decr(nest_ptr); cur_list←nest[nest_ptr];
---
> begin free_avail(head); decr(nest_ptr); cur_list:=nest[nest_ptr];
3793c4296,4297
< @p procedure show_activities;
---
> @p procedure@?print_totals; forward;@t\2@>
> procedure show_activities;
3796c4300
< @!a:integer; {auxiliary}
---
> @!a:memory_word; {auxiliary}
3798,3827c4302,4346
< begin nest[nest_ptr]←cur_list; {put the top level into the array}
< print_nl("");
< for p←nest_ptr downto 0 do
< begin m←nest[p].mode_field; a←nest[p].aux_field;
< print_nl("### "); print_mode(m);
< print(" entered at line "); print_int(abs(nest[p].ml_field));
< if nest[p].ml_field<0 then print(" (\output routine)");
< if p=0 then
< begin print_nl("### current page:");
< @<Show the status of the current page@>;
< print_nl("### recent contributions:");
< end;
< show_box(link(nest[p].head_field));
< case abs(m) div (max_command+1) of
< 0: begin print_nl("prevdepth ");
< if a≤ignore_depth then print("ignored")
< else print_scaled(a);
< if nest[p].already_field≠0 then
< begin print(", already ");
< print_int(nest[p].already_field);
< end;
< end;
< 1: begin print_nl("spacefactor "); print_int(a);
< end;
< 2: if a≠null then
< begin print("this will be denominator of:"); show_box(a);
< end;
< end; {there are no other cases}
< end;
< end;
---
> @!t:integer; {ditto}
> begin nest[nest_ptr]:=cur_list; {put the top level into the array}
> print_nl(""); print_ln;
> for p:=nest_ptr downto 0 do
> begin m:=nest[p].mode_field; a:=nest[p].aux_field;
> print_nl("### "); print_mode(m);
> print(" entered at line "); print_int(abs(nest[p].ml_field));
> if m=hmode then if nest[p].pg_field <> @'40600000 then
> begin print(" (language"); print_int(nest[p].pg_field mod @'200000);
> print(":hyphenmin"); print_int(nest[p].pg_field div @'20000000);
> print_char(","); print_int((nest[p].pg_field div @'200000) mod @'100);
> print_char(")");
> end;
> if nest[p].ml_field<0 then print(" (\output routine)");
> if p=0 then
> begin @<Show the status of the current page@>;
> if link(contrib_head)<>null then
> print_nl("### recent contributions:");
> end;
> show_box(link(nest[p].head_field));
> @<Show the auxiliary field, |a|@>;
> end;
> end;
>
> @ @<Show the auxiliary...@>=
> case abs(m) div (max_command+1) of
> 0: begin print_nl("prevdepth ");
> if a.sc<=ignore_depth then print("ignored")
> else print_scaled(a.sc);
> if nest[p].pg_field<>0 then
> begin print(", prevgraf ");
> print_int(nest[p].pg_field); print(" line");
> if nest[p].pg_field<>1 then print_char("s");
> end;
> end;
> 1: begin print_nl("spacefactor "); print_int(a.hh.lh);
> if m>0 then@+ if a.hh.rh>0 then
> begin print(", current language "); print_int(a.hh.rh);@+
> end;
> end;
> 2: if a.int<>null then
> begin print("this will be denominator of:"); show_box(a.int);@+
> end;
> end {there are no other cases}
>
3830,3831c4349,4351
< let us consider the data structures used by its syntactic routines. In
< other words, we turn now to the tables that \TeX\ looks at when it is scanning
---
> we ought to consider the data structures used by its syntactic routines. In
> other words, our next concern will be
> the tables that \TeX\ looks at when it is scanning
3835c4355
< current ``equiv\-a\-lents'' of things; i.e., it explains what things mean
---
> current ``equivalents'' of things; i.e., it explains what things mean
3840c4360
< \yskip\hang 1) |eqtb[single_base..(hash_base-1)]| holds the current
---
> \yskip\hangg 1) |eqtb[active_base..(hash_base-1)]| holds the current
3843,3844c4363,4364
< \yskip\hang 2) |eqtb[hash_base..(glue_base-1)]| holds the current
< equivalents of multi-letter control sequences.
---
> \yskip\hangg 2) |eqtb[hash_base..(glue_base-1)]| holds the current
> equivalents of multiletter control sequences.
3846c4366
< \yskip\hang 3) |eqtb[glue_base..(local_base-1)]| holds the current
---
> \yskip\hangg 3) |eqtb[glue_base..(local_base-1)]| holds the current
3849c4369
< \yskip\hang 4) |eqtb[local_base..(int_base-1)]| holds the current
---
> \yskip\hangg 4) |eqtb[local_base..(int_base-1)]| holds the current
3851c4371
< the current ``chcodes,'' the current font, and a pointer to the current
---
> the current ``catcodes,'' the current font, and a pointer to the current
3854c4374
< \yskip\hang 5) |eqtb[int_base..(dimen_base-1)]| holds the current
---
> \yskip\hangg 5) |eqtb[int_base..(dimen_base-1)]| holds the current
3858,3859c4378,4379
< \yskip\hang 6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents
< of fullword di\-men\-sion parameters like the current hsize or amount of
---
> \yskip\hangg 6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents
> of fullword dimension parameters like the current hsize or amount of
3864c4384
< in region@@3 of |eqtb|, while the current meaning of the control sequence
---
> in region~3 of |eqtb|, while the current meaning of the control sequence
3866c4386
< \.{\\let}) appears in region@@2.
---
> \.{\\let}) appears in region~2.
3871c4391
< \yskip\hang 1) The |eq_level| (a quarterword) is the level of grouping at
---
> \yskip\hangg 1) The |eq_level| (a quarterword) is the level of grouping at
3878c4398
< \yskip\hang 2) The |eq_type| (another quarterword) specifies what kind of
---
> \yskip\hangg 2) The |eq_type| (another quarterword) specifies what kind of
3883c4403
< \yskip\hang 3) The |equiv| (a halfword) is the current equivalent value.
---
> \yskip\hangg 3) The |equiv| (a halfword) is the current equivalent value.
3900,3902c4420,4422
< In the first region we have 128 equivalents for single-character control
< sequences, followed by 128 equivalents for ``active characters'' that
< act as control sequences.
---
> In the first region we have 256 equivalents for ``active characters'' that
> act as control sequences, followed by 256 equivalents for single-character
> control sequences.
3904c4424
< Then comes region@@2, which corresponds to the hash table that we will
---
> Then comes region~2, which corresponds to the hash table that we will
3906,3907c4426,4427
< control sequence that is perpetually undefined. There also are three
< locations for special control sequences that are perpetually defined
---
> control sequence that is perpetually undefined. There also are several
> locations for control sequences that are perpetually defined
3910,3914c4430,4450
< @d single_base=1 {beginning of region 1, for single-letter control sequences}
< @d active_base=single_base+128 {equivalents of active characters}
< @d hash_base=active_base+128 {beginning of region 2, for the hash table}
< @d special_control_sequence=hash_base+hash_size {for error recovery}
< @d undefined_control_sequence=hash_base+hash_size+3 {dummy location}
---
> @d active_base=1 {beginning of region 1, for active character equivalents}
> @d single_base=active_base+256 {equivalents of one-character control sequences}
> @d null_cs=single_base+256 {equivalent of \.{\\csname\\endcsname}}
> @d hash_base=null_cs+1 {beginning of region 2, for the hash table}
> @d frozen_control_sequence=hash_base+hash_size {for error recovery}
> @d frozen_protection=frozen_control_sequence {inaccessible but definable}
> @d frozen_cr=frozen_control_sequence+1 {permanent `\.{\\cr}'}
> @d frozen_end_group=frozen_control_sequence+2 {permanent `\.{\\endgroup}'}
> @d frozen_right=frozen_control_sequence+3 {permanent `\.{\\right}'}
> @d frozen_fi=frozen_control_sequence+4 {permanent `\.{\\fi}'}
> @d frozen_end_template=frozen_control_sequence+5 {permanent `\.{\\endtemplate}'}
> @d frozen_endv=frozen_control_sequence+6 {second permanent `\.{\\endtemplate}'}
> @d frozen_relax=frozen_control_sequence+7 {permanent `\.{\\relax}'}
> @d end_write=frozen_control_sequence+8 {permanent `\.{\\endwrite}'}
> @d frozen_dont_expand=frozen_control_sequence+9
> {permanent `\.{\\notexpanded:}'}
> @d frozen_null_font=frozen_control_sequence+10
> {permanent `\.{\\nullfont}'}
> @d font_id_base=frozen_null_font-font_base
> {begins table of 257 permanent font identifiers}
> @d undefined_control_sequence=frozen_null_font+257 {dummy location}
3918,3922c4454,4469
< eq_type(undefined_control_sequence)←undefined_cs;
< equiv(undefined_control_sequence)←null;
< eq_level(undefined_control_sequence)←level_zero;
< for k←single_base to undefined_control_sequence-1 do
< eqtb[k]←eqtb[undefined_control_sequence];
---
> eq_type(undefined_control_sequence):=undefined_cs;
> equiv(undefined_control_sequence):=null;
> eq_level(undefined_control_sequence):=level_zero;
> for k:=active_base to undefined_control_sequence-1 do
> eqtb[k]:=eqtb[undefined_control_sequence];
>
> @ Here is a routine that displays the current meaning of an |eqtb| entry
> in region 1 or~2. (Similar routines for the other regions will appear
> below.)
>
> @<Show equivalent |n|, in region 1 or 2@>=
> begin sprint_cs(n); print_char("="); print_cmd_chr(eq_type(n),equiv(n));
> if eq_type(n)>=call then
> begin print_char(":"); show_token_list(link(equiv(n)),null,32);
> end;
> end
3925c4472
< glue parameters defined here. It is important that the ``mskip''
---
> glue parameters defined here. It is important that the ``muskip''
3928c4475
< @d line_skip_code=0 {interline glue if |baseline_skip| is unfeasible}
---
> @d line_skip_code=0 {interline glue if |baseline_skip| is infeasible}
3931,3945c4478,4495
< @d disp_skip_code=3 {extra glue just above and below displayed math}
< @d disp_a_skip_code=4 {glue above displayed math following short lines}
< @d disp_b_skip_code=5 {glue below displayed math following short lines}
< @d left_skip_code=6 {glue at left of justified lines}
< @d right_skip_code=7 {glue at right of justified lines}
< @d top_skip_code=8 {glue at top of main pages}
< @d split_top_skip_code=9 {glue at top of split pages}
< @d tab_skip_code=10 {glue between aligned entries}
< @d space_skip_code=11 {glue between words (if not |zero_glue|)}
< @d xspace_skip_code=12 {glue after sentences (if not |zero_glue|)}
< @d par_fill_skip_code=13 {glue on last line of paragraph}
< @d thin_mskip_code=14 {thin space in math formula}
< @d med_mskip_code=15 {medium space in math formula}
< @d thick_mskip_code=16 {thick space in math formula}
< @d glue_pars=17 {total number of glue parameters}
---
> @d above_display_skip_code=3 {extra glue just above displayed math}
> @d below_display_skip_code=4 {extra glue just below displayed math}
> @d above_display_short_skip_code=5
> {glue above displayed math following short lines}
> @d below_display_short_skip_code=6
> {glue below displayed math following short lines}
> @d left_skip_code=7 {glue at left of justified lines}
> @d right_skip_code=8 {glue at right of justified lines}
> @d top_skip_code=9 {glue at top of main pages}
> @d split_top_skip_code=10 {glue at top of split pages}
> @d tab_skip_code=11 {glue between aligned entries}
> @d space_skip_code=12 {glue between words (if not |zero_glue|)}
> @d xspace_skip_code=13 {glue after sentences (if not |zero_glue|)}
> @d par_fill_skip_code=14 {glue on last line of paragraph}
> @d thin_mu_skip_code=15 {thin space in math formula}
> @d med_mu_skip_code=16 {medium space in math formula}
> @d thick_mu_skip_code=17 {thick space in math formula}
> @d glue_pars=18 {total number of glue parameters}
3947c4497,4498
< @d local_base=skip_base+256 {beginning of region 6}
---
> @d mu_skip_base=skip_base+256 {table of 256 ``muskip'' registers}
> @d local_base=mu_skip_base+256 {beginning of region 4}
3949a4501
> @d mu_skip(#)==equiv(mu_skip_base+#) {|mem| location of math glue spec}
3954,3956c4506,4509
< @d disp_skip==glue_par(disp_skip_code)
< @d disp_a_skip==glue_par(disp_a_skip_code)
< @d disp_b_skip==glue_par(disp_b_skip_code)
---
> @d above_display_skip==glue_par(above_display_skip_code)
> @d below_display_skip==glue_par(below_display_skip_code)
> @d above_display_short_skip==glue_par(above_display_short_skip_code)
> @d below_display_short_skip==glue_par(below_display_short_skip_code)
3965,3967c4518,4520
< @d thin_mskip==glue_par(thin_mskip_code)
< @d med_mskip==glue_par(med_mskip_code)
< @d thick_mskip==glue_par(thick_mskip_code)
---
> @d thin_mu_skip==glue_par(thin_mu_skip_code)
> @d med_mu_skip==glue_par(med_mu_skip_code)
> @d thick_mu_skip==glue_par(thick_mu_skip_code)
3981,3983c4534,4537
< disp_skip_code: print_esc("dispskip");
< disp_a_skip_code: print_esc("dispaskip");
< disp_b_skip_code: print_esc("dispbskip");
---
> above_display_skip_code: print_esc("abovedisplayskip");
> below_display_skip_code: print_esc("belowdisplayskip");
> above_display_short_skip_code: print_esc("abovedisplayshortskip");
> below_display_short_skip_code: print_esc("belowdisplayshortskip");
3992,3994c4546,4548
< thin_mskip_code: print_esc("thinmskip");
< med_mskip_code: print_esc("medmskip");
< thick_mskip_code: print_esc("thickmskip");
---
> thin_mu_skip_code: print_esc("thinmuskip");
> med_mu_skip_code: print_esc("medmuskip");
> thick_mu_skip_code: print_esc("thickmuskip");
4004c4558
< primitive("lineskip",assign_glue,line_skip_code);@/
---
> primitive("lineskip",assign_glue,glue_base+line_skip_code);@/
4006c4560
< primitive("baselineskip",assign_glue,baseline_skip_code);@/
---
> primitive("baselineskip",assign_glue,glue_base+baseline_skip_code);@/
4008c4562
< primitive("parskip",assign_glue,par_skip_code);@/
---
> primitive("parskip",assign_glue,glue_base+par_skip_code);@/
4010,4016c4564,4574
< primitive("dispskip",assign_glue,disp_skip_code);@/
< @!@:disp_skip_}{\.{\\dispskip} primitive@>
< primitive("dispaskip",assign_glue,disp_a_skip_code);@/
< @!@:disp_a_skip_}{\.{\\dispaskip} primitive@>
< primitive("dispbskip",assign_glue,disp_b_skip_code);@/
< @!@:disp_b_skip_}{\.{\\dispbskip} primitive@>
< primitive("leftskip",assign_glue,left_skip_code);@/
---
> primitive("abovedisplayskip",assign_glue,glue_base+above_display_skip_code);@/
> @!@:above_display_skip_}{\.{\\abovedisplayskip} primitive@>
> primitive("belowdisplayskip",assign_glue,glue_base+below_display_skip_code);@/
> @!@:below_display_skip_}{\.{\\belowdisplayskip} primitive@>
> primitive("abovedisplayshortskip",
> assign_glue,glue_base+above_display_short_skip_code);@/
> @!@:above_display_short_skip_}{\.{\\abovedisplayshortskip} primitive@>
> primitive("belowdisplayshortskip",
> assign_glue,glue_base+below_display_short_skip_code);@/
> @!@:below_display_short_skip_}{\.{\\belowdisplayshortskip} primitive@>
> primitive("leftskip",assign_glue,glue_base+left_skip_code);@/
4018c4576
< primitive("rightskip",assign_glue,right_skip_code);@/
---
> primitive("rightskip",assign_glue,glue_base+right_skip_code);@/
4020c4578
< primitive("topskip",assign_glue,top_skip_code);@/
---
> primitive("topskip",assign_glue,glue_base+top_skip_code);@/
4022c4580
< primitive("splittopskip",assign_glue,split_top_skip_code);@/
---
> primitive("splittopskip",assign_glue,glue_base+split_top_skip_code);@/
4024c4582
< primitive("tabskip",assign_glue,tab_skip_code);@/
---
> primitive("tabskip",assign_glue,glue_base+tab_skip_code);@/
4026c4584
< primitive("spaceskip",assign_glue,space_skip_code);@/
---
> primitive("spaceskip",assign_glue,glue_base+space_skip_code);@/
4028c4586
< primitive("xspaceskip",assign_glue,xspace_skip_code);@/
---
> primitive("xspaceskip",assign_glue,glue_base+xspace_skip_code);@/
4030c4588
< primitive("parfillskip",assign_glue,par_fill_skip_code);@/
---
> primitive("parfillskip",assign_glue,glue_base+par_fill_skip_code);@/
4032,4037c4590,4595
< primitive("thinmskip",assign_glue,thin_mskip_code);@/
< @!@:thin_mskip_}{\.{\\thinskip} primitive@>
< primitive("medmskip",assign_glue,med_mskip_code);@/
< @!@:med_mskip_}{\.{\\medmskip} primitive@>
< primitive("thickmskip",assign_glue,thick_mskip_code);@/
< @!@:thick_mskip_}{\.{\\thickmskip} primitive@>
---
> primitive("thinmuskip",assign_mu_glue,glue_base+thin_mu_skip_code);@/
> @!@:thin_mu_skip_}{\.{\\thinmuskip} primitive@>
> primitive("medmuskip",assign_mu_glue,glue_base+med_mu_skip_code);@/
> @!@:med_mu_skip_}{\.{\\medmuskip} primitive@>
> primitive("thickmuskip",assign_mu_glue,glue_base+thick_mu_skip_code);@/
> @!@:thick_mu_skip_}{\.{\\thickmuskip} primitive@>
4040c4598,4604
< assign_glue:print_skip_param(chr_code);
---
> assign_glue,assign_mu_glue: if chr_code<skip_base then
> print_skip_param(chr_code-glue_base)
> else if chr_code<mu_skip_base then
> begin print_esc("skip"); print_int(chr_code-skip_base);
> end
> else begin print_esc("muskip"); print_int(chr_code-mu_skip_base);
> end;
4045,4048c4609,4626
< equiv(glue_base)←zero_glue; eq_level(glue_base)←level_one;
< eq_type(glue_base)←glue_ref;
< for k←glue_base+1 to local_base-1 do eqtb[k]←eqtb[glue_base];
< glue_ref_count(zero_glue)←glue_ref_count(zero_glue)+local_base-glue_base;
---
> equiv(glue_base):=zero_glue; eq_level(glue_base):=level_one;
> eq_type(glue_base):=glue_ref;
> for k:=glue_base+1 to local_base-1 do eqtb[k]:=eqtb[glue_base];
> glue_ref_count(zero_glue):=glue_ref_count(zero_glue)+local_base-glue_base;
>
> @ @<Show equivalent |n|, in region 3@>=
> if n<skip_base then
> begin print_skip_param(n-glue_base); print_char("=");
> if n<glue_base+thin_mu_skip_code then print_spec(equiv(n),"pt")
> else print_spec(equiv(n),"mu");
> end
> else if n<mu_skip_base then
> begin print_esc("skip"); print_int(n-skip_base); print_char("=");
> print_spec(equiv(n),"pt");
> end
> else begin print_esc("muskip"); print_int(n-mu_skip_base); print_char("=");
> print_spec(equiv(n),"mu");
> end
4051c4629
< bulk of this region is taken up by five tables that are indexed by seven-bit
---
> bulk of this region is taken up by five tables that are indexed by eight-bit
4054c4632,4633
< token parameters, as well as the table of 256 \.{\\box} registers.
---
> token parameters, as well as the tables of \.{\\toks} and \.{\\box}
> registers.
4059c4638,4646
< @d box_base=local_base+3 {table of 256 box registers}
---
> @d every_math_loc=local_base+3 {points to token list for \.{\\everymath}}
> @d every_display_loc=local_base+4 {points to token list for \.{\\everydisplay}}
> @d every_hbox_loc=local_base+5 {points to token list for \.{\\everyhbox}}
> @d every_vbox_loc=local_base+6 {points to token list for \.{\\everyvbox}}
> @d every_job_loc=local_base+7 {points to token list for \.{\\everyjob}}
> @d every_cr_loc=local_base+8 {points to token list for \.{\\everycr}}
> @d err_help_loc=local_base+9 {points to token list for \.{\\errhelp}}
> @d toks_base=local_base+10 {table of 256 token list registers}
> @d box_base=toks_base+256 {table of 256 box registers}
4062,4068c4649,4655
< @d cur_fam_loc=math_font_base+48 {current font family}
< @d ch_code_base=cur_fam_loc+1 {table of 128 character command codes}
< @d math_code_base=ch_code_base+128 {table of 128 math mode mappings}
< @d lc_code_base=math_code_base+128 {table of 128 lower case mappings}
< @d uc_code_base=lc_code_base+128 {table of 128 upper case mappings}
< @d sf_code_base=uc_code_base+128 {table of 128 space factor mappings}
< @d int_base=sf_code_base+128 {beginning of region 5}
---
> @d cat_code_base=math_font_base+48
> {table of 256 command codes (the ``catcodes'')}
> @d lc_code_base=cat_code_base+256 {table of 256 lowercase mappings}
> @d uc_code_base=lc_code_base+256 {table of 256 uppercase mappings}
> @d sf_code_base=uc_code_base+256 {table of 256 spacefactor mappings}
> @d math_code_base=sf_code_base+256 {table of 256 math mode mappings}
> @d int_base=math_code_base+256 {beginning of region 5}
4072a4660,4667
> @d every_math==equiv(every_math_loc)
> @d every_display==equiv(every_display_loc)
> @d every_hbox==equiv(every_hbox_loc)
> @d every_vbox==equiv(every_vbox_loc)
> @d every_job==equiv(every_job_loc)
> @d every_cr==equiv(every_cr_loc)
> @d err_help==equiv(err_help_loc)
> @d toks(#)==equiv(toks_base+#)
4075d4669
< @d cur_fam==equiv(cur_fam_loc)
4077,4078c4671
< @d ch_code(#)==equiv(ch_code_base+#)
< @d math_code(#)==equiv(math_code_base+#)
---
> @d cat_code(#)==equiv(cat_code_base+#)
4081a4675,4676
> @d math_code(#)==equiv(math_code_base+#)
> {Note: |math_code(c)| is the true math code plus |min_halfword|}
4087a4683,4696
> primitive("everymath",assign_toks,every_math_loc);
> @!@:every_math_}{\.{\\everymath} primitive@>
> primitive("everydisplay",assign_toks,every_display_loc);
> @!@:every_display_}{\.{\\everydisplay} primitive@>
> primitive("everyhbox",assign_toks,every_hbox_loc);
> @!@:every_hbox_}{\.{\\everyhbox} primitive@>
> primitive("everyvbox",assign_toks,every_vbox_loc);
> @!@:every_vbox_}{\.{\\everyvbox} primitive@>
> primitive("everyjob",assign_toks,every_job_loc);
> @!@:every_job_}{\.{\\everyjob} primitive@>
> primitive("everycr",assign_toks,every_cr_loc);
> @!@:every_cr_}{\.{\\everycr} primitive@>
> primitive("errhelp",assign_toks,err_help_loc);
> @!@:err_help_}{\.{\\errhelp} primitive@>
4090,4091c4699,4712
< assign_toks: if chr_code=output_routine_loc then print_esc("output")
< else print_esc("everypar");
---
> assign_toks: if chr_code>=toks_base then
> begin print_esc("toks"); print_int(chr_code-toks_base);
> end
> else case chr_code of
> output_routine_loc: print_esc("output");
> every_par_loc: print_esc("everypar");
> every_math_loc: print_esc("everymath");
> every_display_loc: print_esc("everydisplay");
> every_hbox_loc: print_esc("everyhbox");
> every_vbox_loc: print_esc("everyvbox");
> every_job_loc: print_esc("everyjob");
> every_cr_loc: print_esc("everycr");
> othercases print_esc("errhelp")
> endcases;
4097c4718,4722
< conventional interpretation of ascii code.
---
> conventional interpretation of ASCII code. These initial values should
> not be changed when \TeX\ is adapted for use with non-English languages;
> all changes to the initialization conventions should be made in format
> packages, not in \TeX\ itself, so that global interchange of formats is
> possible.
4099c4724,4725
< @d undefined_font==font_base
---
> @d null_font==font_base
> @d var_code==@'70000 {math code meaning ``use the current family''}
4102,4124c4728,4811
< par_shape_ptr←null; eq_type(par_shape_loc)←shape_ref;
< eq_level(par_shape_loc)←level_one;@/
< eqtb[output_routine_loc]←eqtb[undefined_control_sequence];
< eqtb[every_par_loc]←eqtb[undefined_control_sequence];
< box(0)←null; eq_type(box_base)←box_ref; eq_level(box_base)←level_one;
< for k←box_base+1 to box_base+255 do eqtb[k]←eqtb[box_base];
< cur_font←undefined_font; eq_type(cur_font_loc)←data;
< eq_level(cur_font_loc)←level_one;@/
< for k←math_font_base to math_font_base+47 do eqtb[k]←eqtb[cur_font_loc];
< cur_fam←0; eq_type(cur_fam_loc)←data; eq_level(cur_fam_loc)←level_one;@/
< for k←ch_code_base to int_base-1 do eqtb[k]←eqtb[cur_fam_loc];
< for k←0 to 127 do
< begin ch_code(k)←other_char; math_code(k)←k; sf_code(k)←1000;
< end;
< ch_code(carriage_return)←car_ret; ch_code(" ")←spacer; ch_code("\")←escape;
< ch_code(invalid_code)←invalid_char; ch_code(null_code)←ignore;
< for k←"A" to "Z" do
< begin ch_code(k)←letter; ch_code(k+"a"-"A")←letter;@/
< math_code(k)←k+@'70400; math_code(k+"a"-"A")←k+"a"-"A"+@'70400;@/
< lc_code(k)←k+"a"-"A"; lc_code(k+"a"-"A")←k+"a"-"A";@/
< uc_code(k)←k; uc_code(k+"a"-"A")←k;@/
< sf_code(k)←999;
< end;
---
> par_shape_ptr:=null; eq_type(par_shape_loc):=shape_ref;
> eq_level(par_shape_loc):=level_one;@/
> for k:=output_routine_loc to toks_base+255 do
> eqtb[k]:=eqtb[undefined_control_sequence];
> box(0):=null; eq_type(box_base):=box_ref; eq_level(box_base):=level_one;
> for k:=box_base+1 to box_base+255 do eqtb[k]:=eqtb[box_base];
> cur_font:=null_font; eq_type(cur_font_loc):=data;
> eq_level(cur_font_loc):=level_one;@/
> for k:=math_font_base to math_font_base+47 do eqtb[k]:=eqtb[cur_font_loc];
> equiv(cat_code_base):=0; eq_type(cat_code_base):=data;
> eq_level(cat_code_base):=level_one;@/
> for k:=cat_code_base+1 to int_base-1 do eqtb[k]:=eqtb[cat_code_base];
> for k:=0 to 255 do
> begin cat_code(k):=other_char; math_code(k):=hi(k); sf_code(k):=1000;
> end;
> cat_code(carriage_return):=car_ret; cat_code(" "):=spacer;
> cat_code("\"):=escape; cat_code("%"):=comment;
> cat_code(invalid_code):=invalid_char; cat_code(null_code):=ignore;
> for k:="0" to "9" do math_code(k):=hi(k+var_code);
> for k:="A" to "Z" do
> begin cat_code(k):=letter; cat_code(k+"a"-"A"):=letter;@/
> math_code(k):=hi(k+var_code+@"100);
> math_code(k+"a"-"A"):=hi(k+"a"-"A"+var_code+@"100);@/
> lc_code(k):=k+"a"-"A"; lc_code(k+"a"-"A"):=k+"a"-"A";@/
> uc_code(k):=k; uc_code(k+"a"-"A"):=k;@/
> sf_code(k):=999;
> end;
>
> @ @<Show equivalent |n|, in region 4@>=
> if n=par_shape_loc then
> begin print_esc("parshape"); print_char("=");
> if par_shape_ptr=null then print_char("0")
> else print_int(info(par_shape_ptr));
> end
> else if n<toks_base then
> begin print_cmd_chr(assign_toks,n); print_char("=");
> if equiv(n)<>null then show_token_list(link(equiv(n)),null,32);
> end
> else if n<box_base then
> begin print_esc("toks"); print_int(n-toks_base); print_char("=");
> if equiv(n)<>null then show_token_list(link(equiv(n)),null,32);
> end
> else if n<cur_font_loc then
> begin print_esc("box"); print_int(n-box_base); print_char("=");
> if equiv(n)=null then print("void")
> else begin depth_threshold:=0; breadth_max:=1; show_node_list(equiv(n));
> end;
> end
> else if n<cat_code_base then @<Show the font identifier in |eqtb[n]|@>
> else @<Show the halfword code in |eqtb[n]|@>
>
> @ @<Show the font identifier in |eqtb[n]|@>=
> begin if n=cur_font_loc then print("current font")
> else if n<math_font_base+16 then
> begin print_esc("textfont"); print_int(n-math_font_base);
> end
> else if n<math_font_base+32 then
> begin print_esc("scriptfont"); print_int(n-math_font_base-16);
> end
> else begin print_esc("scriptscriptfont"); print_int(n-math_font_base-32);
> end;
> print_char("=");@/
> print_esc(hash[font_id_base+equiv(n)].rh);
> {that's |font_id_text(equiv(n))|}
> end
>
> @ @<Show the halfword code in |eqtb[n]|@>=
> if n<math_code_base then
> begin if n<lc_code_base then
> begin print_esc("catcode"); print_int(n-cat_code_base);
> end
> else if n<uc_code_base then
> begin print_esc("lccode"); print_int(n-lc_code_base);
> end
> else if n<sf_code_base then
> begin print_esc("uccode"); print_int(n-uc_code_base);
> end
> else begin print_esc("sfcode"); print_int(n-sf_code_base);
> end;
> print_char("="); print_int(equiv(n));
> end
> else begin print_esc("mathcode"); print_int(n-math_code_base);
> print_char("="); print_int(ho(equiv(n)));
> end
4128c4815
< |ch_code..sf_code| tables that precede it, since delimiter codes are
---
> |cat_code..math_code| tables that precede it, since delimiter codes are
4130c4817,4819
< halfword. This is what makes region@@5 different from region 4.
---
> halfword. This is what makes region~5 different from region~4. We will
> store the |eq_level| information in an auxiliary array of quarterwords
> that will be defined later.
4137,4171c4826,4878
< @d widow_penalty_code=5 {penalty for creating a widow line}
< @d display_widow_penalty_code=6 {ditto, just before a display}
< @d broken_penalty_code=7 {penalty for breaking a page at a broken line}
< @d bin_op_penalty_code=8 {penalty for breaking after a binary operation}
< @d rel_penalty_code=9 {penalty for breaking after a relation}
< @d pre_display_penalty_code=10
< {penalty for breaking just before a displayed formula}
< @d post_display_penalty_code=11
< {penalty for breaking just after a displayed formula}
< @d inter_line_penalty_code=12 {additional penalty between lines}
< @d double_hyphen_demerits_code=13 {demerits for double hyphen break}
< @d final_hyphen_demerits_code=14 {demerits for final hyphen break}
< @d adj_demerits_code=15 {demerits for adjacent incompatible lines}
< @d mag_code=16 {magnification ratio}
< @d delimiter_factor_code=17 {ratio for variable-size delimiters}
< @d looseness_code=18 {change in number of lines for a paragraph}
< @d time_code=19 {current time of day}
< @d day_code=20 {current day of the month}
< @d month_code=21 {current month of the year}
< @d year_code=22 {current year of our Lord}
< @d show_box_breadth_code=23 {nodes per level in |show_box|}
< @d show_box_depth_code=24 {maximum level in |show_box|}
< @d hbadness_code=25 {show hboxes exceeding this badness}
< @d vbadness_code=26 {show vboxes exceeding this badness}
< @d pause_code=27 {pause after each line is read from a file}
< @d tracing_online_code=28 {show diagnostic output on terminal}
< @d tracing_macros_code=29 {show macros as they are being expanded}
< @d tracing_stats_code=30 {show memory usage if \TeX\ knows it}
< @d tracing_output_code=31 {show boxes when they are shipped out}
< @d tracing_lost_chars_code=32 {show characters that aren't in the font}
< @d tracing_commands_code=33 {show command codes at |big_switch|}
< @d uc_hyph_code=34 {hyphenate words beginning with a capital letter}
< @d output_penalty_code=35 {penalty found at current page break}
< @d hang_after_code=36 {hanging indentation changes after this many lines}
< @d int_pars=37 {total number of integer parameters}
---
> @d club_penalty_code=5 {penalty for creating a club line}
> @d widow_penalty_code=6 {penalty for creating a widow line}
> @d display_widow_penalty_code=7 {ditto, just before a display}
> @d broken_penalty_code=8 {penalty for breaking a page at a broken line}
> @d bin_op_penalty_code=9 {penalty for breaking after a binary operation}
> @d rel_penalty_code=10 {penalty for breaking after a relation}
> @d pre_display_penalty_code=11
> {penalty for breaking just before a displayed formula}
> @d post_display_penalty_code=12
> {penalty for breaking just after a displayed formula}
> @d inter_line_penalty_code=13 {additional penalty between lines}
> @d double_hyphen_demerits_code=14 {demerits for double hyphen break}
> @d final_hyphen_demerits_code=15 {demerits for final hyphen break}
> @d adj_demerits_code=16 {demerits for adjacent incompatible lines}
> @d mag_code=17 {magnification ratio}
> @d delimiter_factor_code=18 {ratio for variable-size delimiters}
> @d looseness_code=19 {change in number of lines for a paragraph}
> @d time_code=20 {current time of day}
> @d day_code=21 {current day of the month}
> @d month_code=22 {current month of the year}
> @d year_code=23 {current year of our Lord}
> @d show_box_breadth_code=24 {nodes per level in |show_box|}
> @d show_box_depth_code=25 {maximum level in |show_box|}
> @d hbadness_code=26 {hboxes exceeding this badness will be shown by |hpack|}
> @d vbadness_code=27 {vboxes exceeding this badness will be shown by |vpack|}
> @d pausing_code=28 {pause after each line is read from a file}
> @d tracing_online_code=29 {show diagnostic output on terminal}
> @d tracing_macros_code=30 {show macros as they are being expanded}
> @d tracing_stats_code=31 {show memory usage if \TeX\ knows it}
> @d tracing_paragraphs_code=32 {show line-break calculations}
> @d tracing_pages_code=33 {show page-break calculations}
> @d tracing_output_code=34 {show boxes when they are shipped out}
> @d tracing_lost_chars_code=35 {show characters that aren't in the font}
> @d tracing_commands_code=36 {show command codes at |big_switch|}
> @d tracing_restores_code=37 {show equivalents when they are restored}
> @d uc_hyph_code=38 {hyphenate words beginning with a capital letter}
> @d output_penalty_code=39 {penalty found at current page break}
> @d max_dead_cycles_code=40 {bound on consecutive dead cycles of output}
> @d hang_after_code=41 {hanging indentation changes after this many lines}
> @d floating_penalty_code=42 {penalty for insertions heldover after a split}
> @d global_defs_code=43 {override \.{\\global} specifications}
> @d cur_fam_code=44 {current family}
> @d escape_char_code=45 {escape character for token output}
> @d default_hyphen_char_code=46 {value of \.{\\hyphenchar} when a font is loaded}
> @d default_skew_char_code=47 {value of \.{\\skewchar} when a font is loaded}
> @d end_line_char_code=48 {character placed at the right end of the buffer}
> @d new_line_char_code=49 {character that prints as |print_ln|}
> @d language_code=50 {current hyphenation table}
> @d left_hyphen_min_code=51 {minimum left hyphenation fragment size}
> @d right_hyphen_min_code=52 {minimum right hyphenation fragment size}
> @d holding_inserts_code=53 {do not remove insertion nodes from \.{\\box255}}
> @d error_context_lines_code=54 {maximum intermediate line pairs shown}
> @d int_pars=55 {total number of integer parameters}
4173,4174c4880,4881
< @d del_code_base=count_base+256 {128 delimiter code mappings}
< @d dimen_base=del_code_base+128 {beginning of region 6}
---
> @d del_code_base=count_base+256 {256 delimiter code mappings}
> @d dimen_base=del_code_base+256 {beginning of region 6}
4183a4891
> @d club_penalty==int_par(club_penalty_code)
4206c4914
< @d pause==int_par(pause_code)
---
> @d pausing==int_par(pausing_code)
4209a4918,4919
> @d tracing_paragraphs==int_par(tracing_paragraphs_code)
> @d tracing_pages==int_par(tracing_pages_code)
4212a4923
> @d tracing_restores==int_par(tracing_restores_code)
4214a4926
> @d max_dead_cycles==int_par(max_dead_cycles_code)
4216,4219c4928,4944
<
< @<Assign the values |depth_threshold←show_box_depth|...@>=
< depth_threshold←show_box_depth;
< breadth_max←show_box_breadth
---
> @d floating_penalty==int_par(floating_penalty_code)
> @d global_defs==int_par(global_defs_code)
> @d cur_fam==int_par(cur_fam_code)
> @d escape_char==int_par(escape_char_code)
> @d default_hyphen_char==int_par(default_hyphen_char_code)
> @d default_skew_char==int_par(default_skew_char_code)
> @d end_line_char==int_par(end_line_char_code)
> @d new_line_char==int_par(new_line_char_code)
> @d language==int_par(language_code)
> @d left_hyphen_min==int_par(left_hyphen_min_code)
> @d right_hyphen_min==int_par(right_hyphen_min_code)
> @d holding_inserts==int_par(holding_inserts_code)
> @d error_context_lines==int_par(error_context_lines_code)
>
> @<Assign the values |depth_threshold:=show_box_depth|...@>=
> depth_threshold:=show_box_depth;
> breadth_max:=show_box_breadth
4229a4955
> club_penalty_code:print_esc("clubpenalty");
4252c4978
< pause_code:print_esc("pause");
---
> pausing_code:print_esc("pausing");
4255a4982,4983
> tracing_paragraphs_code:print_esc("tracingparagraphs");
> tracing_pages_code:print_esc("tracingpages");
4258a4987
> tracing_restores_code:print_esc("tracingrestores");
4260a4990
> max_dead_cycles_code:print_esc("maxdeadcycles");
4261a4992,5004
> floating_penalty_code:print_esc("floatingpenalty");
> global_defs_code:print_esc("globaldefs");
> cur_fam_code:print_esc("fam");
> escape_char_code:print_esc("escapechar");
> default_hyphen_char_code:print_esc("defaulthyphenchar");
> default_skew_char_code:print_esc("defaultskewchar");
> end_line_char_code:print_esc("endlinechar");
> new_line_char_code:print_esc("newlinechar");
> language_code:print_esc("language");
> left_hyphen_min_code:print_esc("lefthyphenmin");
> right_hyphen_min_code:print_esc("righthyphenmin");
> holding_inserts_code:print_esc("holdinginserts");
> error_context_lines_code:print_esc("errorcontextlines");
4269c5012
< primitive("pretolerance",assign_int,pretolerance_code);@/
---
> primitive("pretolerance",assign_int,int_base+pretolerance_code);@/
4271c5014
< primitive("tolerance",assign_int,tolerance_code);@/
---
> primitive("tolerance",assign_int,int_base+tolerance_code);@/
4273c5016
< primitive("linepenalty",assign_int,line_penalty_code);@/
---
> primitive("linepenalty",assign_int,int_base+line_penalty_code);@/
4275c5018
< primitive("hyphenpenalty",assign_int,hyphen_penalty_code);@/
---
> primitive("hyphenpenalty",assign_int,int_base+hyphen_penalty_code);@/
4277c5020
< primitive("exhyphenpenalty",assign_int,ex_hyphen_penalty_code);@/
---
> primitive("exhyphenpenalty",assign_int,int_base+ex_hyphen_penalty_code);@/
4279c5022,5024
< primitive("widowpenalty",assign_int,widow_penalty_code);@/
---
> primitive("clubpenalty",assign_int,int_base+club_penalty_code);@/
> @!@:club_penalty_}{\.{\\clubpenalty} primitive@>
> primitive("widowpenalty",assign_int,int_base+widow_penalty_code);@/
4281c5026,5027
< primitive("displaywidowpenalty",assign_int,display_widow_penalty_code);@/
---
> primitive("displaywidowpenalty",
> assign_int,int_base+display_widow_penalty_code);@/
4283c5029
< primitive("brokenpenalty",assign_int,broken_penalty_code);@/
---
> primitive("brokenpenalty",assign_int,int_base+broken_penalty_code);@/
4285c5031
< primitive("binoppenalty",assign_int,bin_op_penalty_code);@/
---
> primitive("binoppenalty",assign_int,int_base+bin_op_penalty_code);@/
4287c5033
< primitive("relpenalty",assign_int,rel_penalty_code);@/
---
> primitive("relpenalty",assign_int,int_base+rel_penalty_code);@/
4289c5035
< primitive("predisplaypenalty",assign_int,pre_display_penalty_code);@/
---
> primitive("predisplaypenalty",assign_int,int_base+pre_display_penalty_code);@/
4291c5037
< primitive("postdisplaypenalty",assign_int,post_display_penalty_code);@/
---
> primitive("postdisplaypenalty",assign_int,int_base+post_display_penalty_code);@/
4293c5039
< primitive("interlinepenalty",assign_int,inter_line_penalty_code);@/
---
> primitive("interlinepenalty",assign_int,int_base+inter_line_penalty_code);@/
4295c5041,5042
< primitive("doublehyphendemerits",assign_int,double_hyphen_demerits_code);@/
---
> primitive("doublehyphendemerits",
> assign_int,int_base+double_hyphen_demerits_code);@/
4297c5044,5045
< primitive("finalhyphendemerits",assign_int,final_hyphen_demerits_code);@/
---
> primitive("finalhyphendemerits",
> assign_int,int_base+final_hyphen_demerits_code);@/
4299c5047
< primitive("adjdemerits",assign_int,adj_demerits_code);@/
---
> primitive("adjdemerits",assign_int,int_base+adj_demerits_code);@/
4301c5049
< primitive("mag",assign_int,mag_code);@/
---
> primitive("mag",assign_int,int_base+mag_code);@/
4303c5051
< primitive("delimiterfactor",assign_int,delimiter_factor_code);@/
---
> primitive("delimiterfactor",assign_int,int_base+delimiter_factor_code);@/
4305c5053
< primitive("looseness",assign_int,looseness_code);@/
---
> primitive("looseness",assign_int,int_base+looseness_code);@/
4307c5055
< primitive("time",assign_int,time_code);@/
---
> primitive("time",assign_int,int_base+time_code);@/
4309c5057
< primitive("day",assign_int,day_code);@/
---
> primitive("day",assign_int,int_base+day_code);@/
4311c5059
< primitive("month",assign_int,month_code);@/
---
> primitive("month",assign_int,int_base+month_code);@/
4313c5061
< primitive("year",assign_int,year_code);@/
---
> primitive("year",assign_int,int_base+year_code);@/
4315c5063
< primitive("showboxbreadth",assign_int,show_box_breadth_code);@/
---
> primitive("showboxbreadth",assign_int,int_base+show_box_breadth_code);@/
4317c5065
< primitive("showboxdepth",assign_int,show_box_depth_code);@/
---
> primitive("showboxdepth",assign_int,int_base+show_box_depth_code);@/
4319c5067
< primitive("hbadness",assign_int,hbadness_code);@/
---
> primitive("hbadness",assign_int,int_base+hbadness_code);@/
4321c5069
< primitive("vbadness",assign_int,vbadness_code);@/
---
> primitive("vbadness",assign_int,int_base+vbadness_code);@/
4323,4325c5071,5073
< primitive("pause",assign_int,pause_code);@/
< @!@:pause_}{\.{\\pause} primitive@>
< primitive("tracingonline",assign_int,tracing_online_code);@/
---
> primitive("pausing",assign_int,int_base+pausing_code);@/
> @!@:pausing_}{\.{\\pausing} primitive@>
> primitive("tracingonline",assign_int,int_base+tracing_online_code);@/
4327c5075
< primitive("tracingmacros",assign_int,tracing_macros_code);@/
---
> primitive("tracingmacros",assign_int,int_base+tracing_macros_code);@/
4329c5077
< primitive("tracingstats",assign_int,tracing_stats_code);@/
---
> primitive("tracingstats",assign_int,int_base+tracing_stats_code);@/
4331c5079,5083
< primitive("tracingoutput",assign_int,tracing_output_code);@/
---
> primitive("tracingparagraphs",assign_int,int_base+tracing_paragraphs_code);@/
> @!@:tracing_paragraphs_}{\.{\\tracingparagraphs} primitive@>
> primitive("tracingpages",assign_int,int_base+tracing_pages_code);@/
> @!@:tracing_pages_}{\.{\\tracingpages} primitive@>
> primitive("tracingoutput",assign_int,int_base+tracing_output_code);@/
4333c5085
< primitive("tracinglostchars",assign_int,tracing_lost_chars_code);@/
---
> primitive("tracinglostchars",assign_int,int_base+tracing_lost_chars_code);@/
4335c5087
< primitive("tracingcommands",assign_int,tracing_commands_code);@/
---
> primitive("tracingcommands",assign_int,int_base+tracing_commands_code);@/
4337c5089,5091
< primitive("uchyph",assign_int,uc_hyph_code);@/
---
> primitive("tracingrestores",assign_int,int_base+tracing_restores_code);@/
> @!@:tracing_restores_}{\.{\\tracingrestores} primitive@>
> primitive("uchyph",assign_int,int_base+uc_hyph_code);@/
4339c5093
< primitive("outputpenalty",assign_int,output_penalty_code);@/
---
> primitive("outputpenalty",assign_int,int_base+output_penalty_code);@/
4341c5095,5097
< primitive("hangafter",assign_int,hang_after_code);@/
---
> primitive("maxdeadcycles",assign_int,int_base+max_dead_cycles_code);@/
> @!@:max_dead_cycles_}{\.{\\maxdeadcycles} primitive@>
> primitive("hangafter",assign_int,int_base+hang_after_code);@/
4342a5099,5124
> primitive("floatingpenalty",assign_int,int_base+floating_penalty_code);@/
> @!@:floating_penalty_}{\.{\\floatingpenalty} primitive@>
> primitive("globaldefs",assign_int,int_base+global_defs_code);@/
> @!@:global_defs_}{\.{\\globaldefs} primitive@>
> primitive("fam",assign_int,int_base+cur_fam_code);@/
> @!@:fam_}{\.{\\fam} primitive@>
> primitive("escapechar",assign_int,int_base+escape_char_code);@/
> @!@:escape_char_}{\.{\\escapechar} primitive@>
> primitive("defaulthyphenchar",assign_int,int_base+default_hyphen_char_code);@/
> @!@:default_hyphen_char_}{\.{\\defaulthyphenchar} primitive@>
> primitive("defaultskewchar",assign_int,int_base+default_skew_char_code);@/
> @!@:default_skew_char_}{\.{\\defaultskewchar} primitive@>
> primitive("endlinechar",assign_int,int_base+end_line_char_code);@/
> @!@:end_line_char_}{\.{\\endlinechar} primitive@>
> primitive("newlinechar",assign_int,int_base+new_line_char_code);@/
> @!@:new_line_char_}{\.{\\newlinechar} primitive@>
> primitive("language",assign_int,int_base+language_code);@/
> @!@:language_}{\.{\\language} primitive@>
> primitive("lefthyphenmin",assign_int,int_base+left_hyphen_min_code);@/
> @!@:left_hyphen_min_}{\.{\\lefthyphenmin} primitive@>
> primitive("righthyphenmin",assign_int,int_base+right_hyphen_min_code);@/
> @!@:right_hyphen_min_}{\.{\\righthyphenmin} primitive@>
> primitive("holdinginserts",assign_int,int_base+holding_inserts_code);@/
> @!@:holding_inserts_}{\.{\\holdinginserts} primitive@>
> primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/
> @!@:error_context_lines_}{\.{\\errorcontextlines} primitive@>
4345c5127,5129
< assign_int: print_param(chr_code);
---
> assign_int: if chr_code<count_base then print_param(chr_code-int_base)
> else begin print_esc("count"); print_int(chr_code-count_base);
> end;
4349a5134
> @^null delimiter@>
4352,4354c5137,5141
< for k←int_base to del_code_base-1 do eqtb[k].int←0;
< mag←1000; tolerance←10000;
< for k←0 to 127 do del_code(k)←-1;
---
> for k:=int_base to del_code_base-1 do eqtb[k].int:=0;
> mag:=1000; tolerance:=10000; hang_after:=1; max_dead_cycles:=25;
> escape_char:="\"; end_line_char:=carriage_return;
> for k:=0 to 255 do del_code(k):=-1;
> del_code("."):=0; {this null delimiter is used in error recovery}
4364,4367c5151,5180
< begin time←12*60; {minutes since midnight}
< day←4; {fourth day of the month}
< month←7; {seventh month of the year}
< year←1776; {Anno Domini}
---
> begin time:=12*60; {minutes since midnight}
> day:=4; {fourth day of the month}
> month:=7; {seventh month of the year}
> year:=1776; {Anno Domini}
> end;
>
> @ @<Show equivalent |n|, in region 5@>=
> begin if n<count_base then print_param(n-int_base)
> else if n<del_code_base then
> begin print_esc("count"); print_int(n-count_base);
> end
> else begin print_esc("delcode"); print_int(n-del_code_base);
> end;
> print_char("="); print_int(eqtb[n].int);
> end
>
> @ @<Set variable |c| to the current escape character@>=c:=escape_char
>
> @ @<Character |s| is the current new-line character@>=s=new_line_char
>
> @ \TeX\ is occasionally supposed to print diagnostic information that
> goes only into the transcript file, unless |tracing_online| is positive.
> Here are two routines that adjust the destination of print commands:
>
> @p procedure begin_diagnostic; {prepare to do some tracing}
> begin old_setting:=selector;
> if (tracing_online<=0)and(selector=term_and_log) then
> begin decr(selector);
> if history=spotless then history:=warning_issued;
> end;
4368a5182,5194
> @#
> procedure end_diagnostic(@!blank_line:boolean);
> {restore proper conditions after tracing}
> begin print_nl("");
> if blank_line then print_ln;
> selector:=old_setting;
> end;
>
> @ Of course we had better declare another global variable, if the previous
> routines are going to work.
>
> @<Glob...@>=
> @!old_setting:0..max_selector;
4371,4372c5197
< here, and the 256 \.{\\dimen} registers. It also contains the current
< amount of hanging indentation, which is slightly special.
---
> here, and the 256 \.{\\dimen} registers.
4376,4381c5201,5206
< @d var_unit_code=2 {variable unit for dimensions}
< @d line_skip_limit_code=3 {threshold for |line_skip| instead of |baseline_skip|}
< @d hsize_code=4 {line width in horizontal mode}
< @d vsize_code=5 {page height in vertical mode}
< @d max_depth_code=6 {maximum depth of boxes on main pages}
< @d split_max_depth_code=7 {maximum depth of boxes on split pages}
---
> @d line_skip_limit_code=2 {threshold for |line_skip| instead of |baseline_skip|}
> @d hsize_code=3 {line width in horizontal mode}
> @d vsize_code=4 {page height in vertical mode}
> @d max_depth_code=5 {maximum depth of boxes on main pages}
> @d split_max_depth_code=6 {maximum depth of boxes on split pages}
> @d box_max_depth_code=7 {maximum depth of explicit vboxes}
4384c5209
< @d delimiter_limit_code=10 {maximum amount uncovered by variable delimiters}
---
> @d delimiter_shortfall_code=10 {maximum amount uncovered by variable delimiters}
4391,4394c5216,5222
< @d dimen_pars=17 {total number of dimension parameters}
< @d hanging_indent_code=dimen_pars {amount of hanging indentation}
< @d scaled_base=dimen_base+dimen_pars+1
< {table of 256 user-defined \.{\\dimen} registers}
---
> @d hang_indent_code=17 {amount of hanging indentation}
> @d h_offset_code=18 {amount of horizontal offset when shipping pages out}
> @d v_offset_code=19 {amount of vertical offset when shipping pages out}
> @d emergency_stretch_code=20 {reduces badnesses on final pass of line-breaking}
> @d dimen_pars=21 {total number of dimension parameters}
> @d scaled_base=dimen_base+dimen_pars
> {table of 256 user-defined \.{\\dimen} registers}
4401d5228
< @d var_unit==dimen_par(var_unit_code)
4406a5234
> @d box_max_depth==dimen_par(box_max_depth_code)
4409c5237
< @d delimiter_limit==dimen_par(delimiter_limit_code)
---
> @d delimiter_shortfall==dimen_par(delimiter_shortfall_code)
4416c5244,5247
< @d hanging_indent==dimen_par(hanging_indent_code)
---
> @d hang_indent==dimen_par(hang_indent_code)
> @d h_offset==dimen_par(h_offset_code)
> @d v_offset==dimen_par(v_offset_code)
> @d emergency_stretch==dimen_par(emergency_stretch_code)
4422d5252
< var_unit_code:print_esc("varunit");
4427a5258
> box_max_depth_code:print_esc("boxmaxdepth");
4430c5261
< delimiter_limit_code:print_esc("delimiterlimit");
---
> delimiter_shortfall_code:print_esc("delimitershortfall");
4436a5268,5271
> hang_indent_code:print_esc("hangindent");
> h_offset_code:print_esc("hoffset");
> v_offset_code:print_esc("voffset");
> emergency_stretch_code:print_esc("emergencystretch");
4442c5277
< primitive("parindent",assign_dimen,par_indent_code);@/
---
> primitive("parindent",assign_dimen,dimen_base+par_indent_code);@/
4444c5279
< primitive("mathsurround",assign_dimen,math_surround_code);@/
---
> primitive("mathsurround",assign_dimen,dimen_base+math_surround_code);@/
4446,4448c5281
< primitive("varunit",assign_dimen,var_unit_code);@/
< @!@:var_unit_}{\.{\\varunit} primitive@>
< primitive("lineskiplimit",assign_dimen,line_skip_limit_code);@/
---
> primitive("lineskiplimit",assign_dimen,dimen_base+line_skip_limit_code);@/
4450c5283
< primitive("hsize",assign_dimen,hsize_code);@/
---
> primitive("hsize",assign_dimen,dimen_base+hsize_code);@/
4452c5285
< primitive("vsize",assign_dimen,vsize_code);@/
---
> primitive("vsize",assign_dimen,dimen_base+vsize_code);@/
4454c5287
< primitive("maxdepth",assign_dimen,max_depth_code);@/
---
> primitive("maxdepth",assign_dimen,dimen_base+max_depth_code);@/
4456c5289
< primitive("splitmaxdepth",assign_dimen,split_max_depth_code);@/
---
> primitive("splitmaxdepth",assign_dimen,dimen_base+split_max_depth_code);@/
4458c5291,5293
< primitive("hfuzz",assign_dimen,hfuzz_code);@/
---
> primitive("boxmaxdepth",assign_dimen,dimen_base+box_max_depth_code);@/
> @!@:box_max_depth_}{\.{\\boxmaxdepth} primitive@>
> primitive("hfuzz",assign_dimen,dimen_base+hfuzz_code);@/
4460c5295
< primitive("vfuzz",assign_dimen,vfuzz_code);@/
---
> primitive("vfuzz",assign_dimen,dimen_base+vfuzz_code);@/
4462,4464c5297,5301
< primitive("delimiterlimit",assign_dimen,delimiter_limit_code);@/
< @!@:delimiter_limit_}{\.{\\delimiterlimit} primitive@>
< primitive("nulldelimiterspace",assign_dimen,null_delimiter_space_code);@/
---
> primitive("delimitershortfall",
> assign_dimen,dimen_base+delimiter_shortfall_code);@/
> @!@:delimiter_shortfall_}{\.{\\delimitershortfall} primitive@>
> primitive("nulldelimiterspace",
> assign_dimen,dimen_base+null_delimiter_space_code);@/
4466c5303
< primitive("scriptspace",assign_dimen,script_space_code);@/
---
> primitive("scriptspace",assign_dimen,dimen_base+script_space_code);@/
4468c5305
< primitive("predisplaysize",assign_dimen,pre_display_size_code);@/
---
> primitive("predisplaysize",assign_dimen,dimen_base+pre_display_size_code);@/
4470c5307
< primitive("displaywidth",assign_dimen,display_width_code);@/
---
> primitive("displaywidth",assign_dimen,dimen_base+display_width_code);@/
4472c5309
< primitive("displayindent",assign_dimen,display_indent_code);@/
---
> primitive("displayindent",assign_dimen,dimen_base+display_indent_code);@/
4474c5311
< primitive("overfullrule",assign_dimen,overfull_rule_code);@/
---
> primitive("overfullrule",assign_dimen,dimen_base+overfull_rule_code);@/
4475a5313,5320
> primitive("hangindent",assign_dimen,dimen_base+hang_indent_code);@/
> @!@:hang_indent_}{\.{\\hangindent} primitive@>
> primitive("hoffset",assign_dimen,dimen_base+h_offset_code);@/
> @!@:h_offset_}{\.{\\hoffset} primitive@>
> primitive("voffset",assign_dimen,dimen_base+v_offset_code);@/
> @!@:v_offset_}{\.{\\voffset} primitive@>
> primitive("emergencystretch",assign_dimen,dimen_base+emergency_stretch_code);@/
> @!@:emergency_stretch_}{\.{\\emergencystretch} primitive@>
4478c5323,5326
< assign_dimen:print_length_param(chr_code);
---
> assign_dimen: if chr_code<scaled_base then
> print_length_param(chr_code-dimen_base)
> else begin print_esc("dimen"); print_int(chr_code-scaled_base);
> end;
4481c5329,5351
< for k←dimen_base to eqtb_size do eqtb[k].sc←0;
---
> for k:=dimen_base to eqtb_size do eqtb[k].sc:=0;
>
> @ @<Show equivalent |n|, in region 6@>=
> begin if n<scaled_base then print_length_param(n-dimen_base)
> else begin print_esc("dimen"); print_int(n-scaled_base);
> end;
> print_char("="); print_scaled(eqtb[n].sc); print("pt");
> end
>
> @ Here is a procedure that displays the contents of |eqtb[n]|
> symbolically.
>
> @p@t\4@>@<Declare the procedure called |print_cmd_chr|@>@;@/
> @!stat procedure show_eqtb(@!n:pointer);
> begin if n<active_base then print_char("?") {this can't happen}
> else if n<glue_base then @<Show equivalent |n|, in region 1 or 2@>
> else if n<local_base then @<Show equivalent |n|, in region 3@>
> else if n<int_base then @<Show equivalent |n|, in region 4@>
> else if n<dimen_base then @<Show equivalent |n|, in region 5@>
> else if n<=eqtb_size then @<Show equivalent |n|, in region 6@>
> else print_char("?"); {this can't happen either}
> end;
> tats
4488,4489c5358,5359
< @<Globals...@>=
< @!eqtb:array[single_base..eqtb_size] of memory_word;
---
> @<Glob...@>=
> @!eqtb:array[active_base..eqtb_size] of memory_word;
4493c5363
< for k←int_base to eqtb_size do xeq_level[k]←level_one;
---
> for k:=int_base to eqtb_size do xeq_level[k]:=level_one;
4496,4497c5366,5367
< given value, it is interested only in regions 1 to@@3 of@@|eqtb|, and in the
< first part of region@@4.
---
> given value, it is interested only in regions 1 to~3 of~|eqtb|, and in the
> first part of region~4.
4500,4522c5370,5374
< for q←single_base to box_base+255 do
< begin if equiv(q)=p then
< begin print_nl("EQUIV("); print_int(q); print_char(")");
< end;
< end
<
< @ \TeX\ is occasionally supposed to print diagnostic information that
< goes only into the transcript file, unless |tracing_online| is nonzero.
< Here are two routines that adjust the destination of print commands:
<
< @p procedure begin_diagnostic; {prepare to do some tracing}
< begin old_setting←selector; save_offset←offset;
< if (tracing_online=0)∧(selector=term_and_err) then decr(selector);
< end;
< @#
< procedure end_diagnostic; {restore proper conditions after tracing}
< begin if offset>0 then print_ln;
< selector←old_setting;
< if (tracing_online=0)∧(old_setting=term_and_err) then offset←save_offset;
< end;
<
< @ Of course we had better declare two more global variables, if the previous
< routines are going to work.
---
> for q:=active_base to box_base+255 do
> begin if equiv(q)=p then
> begin print_nl("EQUIV("); print_int(q); print_char(")");
> end;
> end
4524,4526d5375
< @<Glob...@>=
< @!old_setting:0..max_selector;
< @!save_offset:0..max_print_line;
4531c5380
< table, it is never removed again, because there are complicated situations
---
> table, it is never removed, because there are complicated situations
4540c5389
< belonging to the same coalesced list as the identifier corresponding to@@|p|;
---
> belonging to the same coalesced list as the identifier corresponding to~|p|;
4542c5391
< |p|'s identifier. If position@@|p| of the hash table is empty, we have
---
> |p|'s identifier. If position~|p| of the hash table is empty, we have
4545,4546c5394,5395
< |hash_used| is maintained in such a way that all locations |p≥hash_used|
< are nonempty. The global variable |cs_count| tells how many multi-letter
---
> |hash_used| is maintained in such a way that all locations |p>=hash_used|
> are nonempty. The global variable |cs_count| tells how many multiletter
4550,4552c5399
< |true| during the time that new hash table entries are forbidden. Another
< global variable, |cs_ptr|, records the position in |eqtb| of the most recently
< found control sequence.
---
> |true| during the time that new hash table entries are forbidden.
4556a5404
> @d font_id_text(#) == text(font_id_base+#) {a frozen font identifier's name}
4558c5406
< @<Globals...@>=
---
> @<Glob...@>=
4560c5408
< {the hash table}
---
> {the hash table}
4563d5410
< @!cs_ptr:pointer; {control sequence found here, zero if none found}
4567,4569c5414,5416
< no_new_control_sequence←true; {new identifiers are usually forbidden}
< next(hash_base)←0; text(hash_base)←0;
< for k←hash_base+1 to undefined_control_sequence-1 do hash[k]←hash[hash_base];
---
> no_new_control_sequence:=true; {new identifiers are usually forbidden}
> next(hash_base):=0; text(hash_base):=0;
> for k:=hash_base+1 to undefined_control_sequence-1 do hash[k]:=hash[hash_base];
4572,4573c5419,5423
< hash_used←special_control_sequence; {nothing is used}
< cs_count←0;
---
> hash_used:=frozen_control_sequence; {nothing is used}
> cs_count:=0;
> eq_type(frozen_dont_expand):=dont_expand;
> text(frozen_dont_expand):="notexpanded:";
> @.notexpanded:@>
4585a5436
> @!d:integer; {number of characters in incomplete current string}
4589,4601c5440,5452
< p←h+hash_base; {we start searching here; note that |0≤h<hash_prime|}
< loop@+begin if (text(p)>0) and (length(text(p))=l) then
< if str_eq_buf(text(p),j) then goto found;
< if next(p)=0 then
< begin if no_new_control_sequence then
< p←undefined_control_sequence
< else @<Insert a new control sequence after |p|, then make
< |p| point to it@>;
< goto found;
< end;
< p←next(p);
< end;
< found: id_lookup←p;
---
> p:=h+hash_base; {we start searching here; note that |0<=h<hash_prime|}
> loop@+begin if text(p)>0 then if length(text(p))=l then
> if str_eq_buf(text(p),j) then goto found;
> if next(p)=0 then
> begin if no_new_control_sequence then
> p:=undefined_control_sequence
> else @<Insert a new control sequence after |p|, then make
> |p| point to it@>;
> goto found;
> end;
> p:=next(p);
> end;
> found: id_lookup:=p;
4606,4614c5457,5469
< begin repeat if hash_is_full then overflow("hash size",hash_size);
< decr(hash_used);
< until text(hash_used)=0; {search for an empty location in |hash|}
< next(p)←hash_used; p←hash_used;
< end;
< str_room(l);
< for k←j to j+l-1 do append_char(buffer[k]);
< text(p)←make_string;
< stat incr(cs_count);@+tats@;@/
---
> begin repeat if hash_is_full then overflow("hash size",hash_size);
> @:TeX capacity exceeded hash size}{\quad hash size@>
> decr(hash_used);
> until text(hash_used)=0; {search for an empty location in |hash|}
> next(p):=hash_used; p:=hash_used;
> end;
> str_room(l); d:=cur_length;
> while pool_ptr>str_start[str_ptr] do
> begin decr(pool_ptr); str_pool[pool_ptr+l]:=str_pool[pool_ptr];
> end; {move current string up to make room for another}
> for k:=j to j+l-1 do append_char(buffer[k]);
> text(p):=make_string; pool_ptr:=pool_ptr+d;
> @!stat incr(cs_count);@+tats@;@/
4617c5472
< @ The value of |hash_prime| should be roughly 85\%\ of |hash_size|, and it
---
> @ The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it
4619a5475,5476
> [See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.]
> @^Vitter, Jeffrey Scott@>
4622,4626c5479,5483
< h←buffer[j];
< for k←j+1 to j+l-1 do
< begin h←h+h+buffer[k];
< while h≥hash_prime do h←h-hash_prime;
< end
---
> h:=buffer[j];
> for k:=j+1 to j+l-1 do
> begin h:=h+h+buffer[k];
> while h>=hash_prime do h:=h-hash_prime;
> end
4632,4633c5489,5492
< if it consists entirely of letters. This procedure might be invoked with
< invalid data, so it is ``extra robust''.
---
> unless it is a single nonletter or an active character. This procedure
> might be invoked with invalid data, so it is ``extra robust.'' The
> individual characters must be printed one at a time using |print|, since
> they may be unprintable.
4636c5495
< procedure print_cs(@!p:pointer); {prints a purported control sequence}
---
> procedure print_cs(@!p:integer); {prints a purported control sequence}
4638,4646c5497,5512
< if p≥active_base then print(p-active_base)
< else if p<single_base then print_esc("IMPOSSIBLE.")
< else begin print_esc(p-single_base);
< if ch_code(p)=letter then print_char(" ");
< end
< else if p≥undefined_control_sequence then print_esc("IMPOSSIBLE.")
< else if text(p)=0 then print_esc("NONEXISTENT.")
< else begin print_esc(text(p)); print_char(" ");
< end;
---
> if p>=single_base then
> if p=null_cs then
> begin print_esc("csname"); print_esc("endcsname"); print_char(" ");
> end
> else begin print_esc(p-single_base);
> if cat_code(p-single_base)=letter then print_char(" ");
> end
> else if p<active_base then print_esc("IMPOSSIBLE.")
> @.IMPOSSIBLE@>
> else print(p-active_base)
> else if p>=undefined_control_sequence then print_esc("IMPOSSIBLE.")
> else if (text(p)<0)or(text(p)>=str_ptr) then print_esc("NONEXISTENT.")
> @.NONEXISTENT@>
> else begin print_esc(text(p));
> print_char(" ");
> end;
4652c5518,5519
< @p procedure sprint_cs(@!p:pointer); {prints a control sequence}
---
> @<Basic printing procedures@>=
> procedure sprint_cs(@!p:pointer); {prints a control sequence}
4654,4655c5521,5524
< if p<active_base then print_esc(p-single_base)
< else print(p-active_base)
---
> if p<single_base then print(p-active_base)
> else if p<null_cs then print_esc(p-single_base)
> else begin print_esc("csname"); print_esc("endcsname");
> end
4665c5534
< @p init procedure primitive(@!s:str_number;@!c:quarterword;@!o:halfword);
---
> @p @!init procedure primitive(@!s:str_number;@!c:quarterword;@!o:halfword);
4669,4676c5538,5545
< begin if s<128 then cur_val←s+single_base
< else begin k←str_start[s]; l←str_start[s+1]-k;
< {we will move |s| into the (empty) |buffer|}
< for j←0 to l-1 do buffer[j]←str_pool[k+j];
< cur_val←id_lookup(0,l); {|no_new_control_sequence| is |false|}
< flush_string; text(cur_val)←s; {we don't want to have the string twice}
< end;
< eq_level(cur_val)←level_one; eq_type(cur_val)←c; equiv(cur_val)←o;
---
> begin if s<256 then cur_val:=s+single_base
> else begin k:=str_start[s]; l:=str_start[s+1]-k;
> {we will move |s| into the (empty) |buffer|}
> for j:=0 to l-1 do buffer[j]:=so(str_pool[k+j]);
> cur_val:=id_lookup(0,l); {|no_new_control_sequence| is |false|}
> flush_string; text(cur_val):=s; {we don't want to have the string twice}
> end;
> eq_level(cur_val):=level_one; eq_type(cur_val):=c; equiv(cur_val):=o;
4685,4688c5554,5567
< primitive("relax",relax,0);@/
< @!@:relax_}{\.{\\relax} primitive@>
< primitive("let",let,0);@/
< @!@:let_}{\.{\\let} primitive@>
---
> primitive(" ",ex_space,0);@/
> @!@:Single-character primitives /}{\quad\.{\\\ }@>
> primitive("/",ital_corr,0);@/
> @!@:Single-character primitives /}{\quad\.{\\/}@>
> primitive("accent",accent,0);@/
> @!@:accent_}{\.{\\accent} primitive@>
> primitive("advance",advance,0);@/
> @!@:advance_}{\.{\\advance} primitive@>
> primitive("afterassignment",after_assignment,0);@/
> @!@:after_assignment_}{\.{\\afterassignment} primitive@>
> primitive("aftergroup",after_group,0);@/
> @!@:after_group_}{\.{\\aftergroup} primitive@>
> primitive("begingroup",begin_group,0);@/
> @!@:begin_group_}{\.{\\begingroup} primitive@>
4691,4698c5570,5582
< primitive("mathchar",math_char_num,0);@/
< @!@:math_char_}{\.{\\mathchar} primitive@>
< primitive("mark",mark,0);@/
< @!@:mark_}{\.{\\mark} primitive@>
< primitive("input",input,0);@/
< @!@:input_}{\.{\\input} primitive@>
< primitive("penalty",break_penalty,0);@/
< @!@:penalty_}{\.{\\penalty} primitive@>
---
> primitive("csname",cs_name,0);@/
> @!@:cs_name_}{\.{\\csname} primitive@>
> primitive("delimiter",delim_num,0);@/
> @!@:delimiter_}{\.{\\delimiter} primitive@>
> primitive("divide",divide,0);@/
> @!@:divide_}{\.{\\divide} primitive@>
> primitive("endcsname",end_cs_name,0);@/
> @!@:end_cs_name_}{\.{\\endcsname} primitive@>
> primitive("endgroup",end_group,0);
> @!@:end_group_}{\.{\\endgroup} primitive@>
> text(frozen_end_group):="endgroup"; eqtb[frozen_end_group]:=eqtb[cur_val];@/
> primitive("expandafter",expand_after,0);@/
> @!@:expand_after_}{\.{\\expandafter} primitive@>
4701,4714c5585,5586
< primitive(":",set_font,0);@/
< @!@:Single-character primitives :}{\quad\.{\\:}@>
< primitive("fam",set_family,0);@/
< @!@:fam_}{\.{\\fam} primitive@>
< primitive("number",number,0);@/
< @!@:number_}{\.{\\number} primitive@>
< primitive("setbox",set_box,0);@/
< @!@:set_box_}{\.{\\setbox} primitive@>
< primitive("unbox",unbox,0);@/
< @!@:unbox_}{\.{\\unbox} primitive@>
< primitive("unskip",unskip,0);@/
< @!@:unskip_}{\.{\\unskip} primitive@>
< primitive("lastskip",last_skip,0);@/
< @!@:last_skip_}{\.{\\lastskip} primitive@>
---
> primitive("fontdimen",assign_font_dimen,0);@/
> @!@:font_dimen_}{\.{\\fontdimen} primitive@>
4717,4722d5588
< primitive("valign",valign,0);@/
< @!@:valign_}{\.{\\valign} primitive@>
< primitive("noalign",no_align,0);@/
< @!@:no_align_}{\.{\\noalign} primitive@>
< primitive("vrule",vrule,0);@/
< @!@:vrule_}{\.{\\vrule} primitive@>
4724a5591,5592
> primitive("ignorespaces",ignore_spaces,0);@/
> @!@:ignore_spaces_}{\.{\\ignorespaces} primitive@>
4727,4736c5595,5596
< primitive("vadjust",vadjust,0);@/
< @!@:vadjust_}{\.{\\vadjust} primitive@>
< primitive("ignorespace",ignore_space,0);@/
< @!@:ignore_space_}{\.{\\ignorespace} primitive@>
< primitive("parshape",set_shape,0);@/
< @!@:par_shape_}{\.{\\parshape} primitive@>
< primitive("/",ital_corr,0);@/
< @!@:Single-character primitives /}{\quad\.{\\/}@>
< primitive("accent",accent,0);@/
< @!@:accent_}{\.{\\accent} primitive@>
---
> primitive("mark",mark,0);@/
> @!@:mark_}{\.{\\mark} primitive@>
4739,4744c5599,5610
< primitive("texinfo",assign_tex_info,0);@/
< @!@:tex_info_}{\.{\\texinfo} primitive@>
< primitive("delimiter",delim_num,0);@/
< @!@:delimiter_}{\.{\\delimiter} primitive@>
< primitive("limitswitch",limit_switch,0);@/
< @!@:limit_switch_}{\.{\\limitswitch} primitive@>
---
> primitive("mathchar",math_char_num,0);@/
> @!@:math_char_}{\.{\\mathchar} primitive@>
> primitive("mathchoice",math_choice,0);@/
> @!@:math_choice_}{\.{\\mathchoice} primitive@>
> primitive("multiply",multiply,0);@/
> @!@:multiply_}{\.{\\multiply} primitive@>
> primitive("noalign",no_align,0);@/
> @!@:no_align_}{\.{\\noalign} primitive@>
> primitive("noboundary",no_boundary,0);@/
> @!@:no_boundary_}{\.{\\noboundary} primitive@>
> primitive("noexpand",no_expand,0);@/
> @!@:no_expand_}{\.{\\noexpand} primitive@>
4747,4752d5612
< primitive("vcenter",vcenter,0);@/
< @!@:vcenter_}{\.{\\vcenter} primitive@>
< primitive("case",case_branch,0);@/
< @!@:case_}{\.{\\case} primitive@>
< primitive("else",else_code,0);@/
< @!@:else_}{\.{\\else} primitive@>
4755,4760c5615,5620
< primitive("groupbegin",group_begin,0);@/
< @!@:group_begin_}{\.{\\groupbegin} primitive@>
< primitive("groupend",group_end,0);@/
< @!@:group_end_}{\.{\\groupend} primitive@>
< primitive(" ",ex_space,0);@/
< @!@:Single-character primitives /}{\quad\.{\\\ }@>
---
> primitive("parshape",set_shape,0);@/
> @!@:par_shape_}{\.{\\parshape} primitive@>
> primitive("penalty",break_penalty,0);@/
> @!@:penalty_}{\.{\\penalty} primitive@>
> primitive("prevgraf",set_prev_graf,0);@/
> @!@:prev_graf_}{\.{\\prevgraf} primitive@>
4762a5623,5641
> primitive("read",read_to_cs,0);@/
> @!@:read_}{\.{\\read} primitive@>
> primitive("relax",relax,256); {cf.\ |scan_file_name|}
> @!@:relax_}{\.{\\relax} primitive@>
> text(frozen_relax):="relax"; eqtb[frozen_relax]:=eqtb[cur_val];@/
> primitive("setbox",set_box,0);@/
> @!@:set_box_}{\.{\\setbox} primitive@>
> primitive("the",the,0);@/
> @!@:the_}{\.{\\the} primitive@>
> primitive("toks",toks_register,0);@/
> @!@:toks_}{\.{\\toks} primitive@>
> primitive("vadjust",vadjust,0);@/
> @!@:vadjust_}{\.{\\vadjust} primitive@>
> primitive("valign",valign,0);@/
> @!@:valign_}{\.{\\valign} primitive@>
> primitive("vcenter",vcenter,0);@/
> @!@:vcenter_}{\.{\\vcenter} primitive@>
> primitive("vrule",vrule,0);@/
> @!@:vrule_}{\.{\\vrule} primitive@>
4768c5647
< explained below.
---
> below.
4771,4776c5650,5655
< relax: print_esc("relax");
< let: print_esc("let");
< char_num: print_esc("char");
< math_char_num: print_esc("mathchar");
< mark: print_esc("mark");
< input: print_esc("input");
---
> accent: print_esc("accent");
> advance: print_esc("advance");
> after_assignment: print_esc("afterassignment");
> after_group: print_esc("aftergroup");
> assign_font_dimen: print_esc("fontdimen");
> begin_group: print_esc("begingroup");
4777a5657,5658
> char_num: print_esc("char");
> cs_name: print_esc("csname");
4779,4785c5660,5665
< set_font: print_esc(":");
< set_family: print_esc("fam");
< number: print_esc("number");
< set_box: print_esc("setbox");
< unbox: print_esc("unbox");
< unskip: print_esc("unskip");
< last_skip: print_esc("lastskip");
---
> delim_num: print_esc("delimiter");
> divide: print_esc("divide");
> end_cs_name: print_esc("endcsname");
> end_group: print_esc("endgroup");
> ex_space: print_esc(" ");
> expand_after: print_esc("expandafter");
4787,4789d5666
< valign: print_esc("valign");
< no_align: print_esc("noalign");
< vrule: print_esc("vrule");
4790a5668
> ignore_spaces: print_esc("ignorespaces");
4792,4794d5669
< vadjust: print_esc("vadjust");
< ignore_space: print_esc("ignorespace");
< set_shape: print_esc("parshape");
4796c5671
< accent: print_esc("accent");
---
> mark: print_esc("mark");
4798,4800c5673,5678
< assign_tex_info: print_esc("texinfo");
< delim_num: print_esc("delimiter");
< limit_switch: print_esc("limitswitch");
---
> math_char_num: print_esc("mathchar");
> math_choice: print_esc("mathchoice");
> multiply: print_esc("multiply");
> no_align: print_esc("noalign");
> no_boundary:print_esc("noboundary");
> no_expand: print_esc("noexpand");
4802,4804d5679
< vcenter: print_esc("vcenter");
< case_branch: print_esc("case");
< else_code: print_esc("else");
4806,4808d5680
< group_begin: print_esc("groupbegin");
< group_end: print_esc("groupend");
< ex_space: print_esc(" ");
4810,4813c5682,5696
<
< @ We will deal with the other primitives later, at some point in the program
< where their |eq_type| and |equiv| values are more meaningful. For example,
< the primitives for math mode will be loaded when we consider the routines
---
> read_to_cs: print_esc("read");
> relax: print_esc("relax");
> set_box: print_esc("setbox");
> set_prev_graf: print_esc("prevgraf");
> set_shape: print_esc("parshape");
> the: print_esc("the");
> toks_register: print_esc("toks");
> vadjust: print_esc("vadjust");
> valign: print_esc("valign");
> vcenter: print_esc("vcenter");
> vrule: print_esc("vrule");
>
> @ We will deal with the other primitives later, at some point in the program
> where their |eq_type| and |equiv| values are more meaningful. For example,
> the primitives for math mode will be loaded when we consider the routines
4816c5699
< module where |"radical"| entered |eqtb| is listed under `\.{\\radical}
---
> section where |"radical"| entered |eqtb| is listed under `\.{\\radical}
4819a5703,5709
>
> Meanwhile, this is a convenient place to catch up on something we were unable
> to do before the hash table was defined:
>
> @<Print the font identifier for |font(p)|@>=
> print_esc(font_id_text(font(p)))
>
4821c5711
< The nested structure provided by `$\.{\char'173}\ldotsm\.{\char'175}$' groups
---
> The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups
4834c5724
< interpreted in one of three ways:
---
> interpreted in one of four ways:
4836c5726
< \yskip\hang 1) If |save_type(p)=restore_old_value|, then
---
> \yskip\hangg 1) If |save_type(p)=restore_old_value|, then
4839c5729
< Furthermore if |save_index(p)≥int_base|, then |save_level(p)|
---
> Furthermore if |save_index(p)>=int_base|, then |save_level(p)|
4842c5732
< \yskip\hang 2) If |save_type(p)=restore_zero|, then |save_index(p)|
---
> \yskip\hangg 2) If |save_type(p)=restore_zero|, then |save_index(p)|
4844c5734,5735
< of the current group and replaced by |eqtb[undefined_control_sequence]|.
---
> of the current group, when it should be
> replaced by the current value of |eqtb[undefined_control_sequence]|.
4846c5737,5741
< \yskip\hang 3) If |save_type(p)=level_boundary|, then |save_level(p)|
---
> \yskip\hangg 3) If |save_type(p)=insert_token|, then |save_index(p)|
> is a token that should be inserted into \TeX's input when the current
> group ends.
>
> \yskip\hangg 4) If |save_type(p)=level_boundary|, then |save_level(p)|
4853c5748
< {saved level for regions 6 and 7, or group code}
---
> {saved level for regions 5 and 6, or group code}
4855c5750
< {|eqtb| location or |save_stack| location}
---
> {|eqtb| location or token or |save_stack| location}
4858c5753,5754
< @d level_boundary=2 {|save_type| corresponding to beginning of group}
---
> @d insert_token=2 {|save_type| when a token is being saved for later use}
> @d level_boundary=3 {|save_type| corresponding to beginning of group}
4866c5762
< the `\.\$' that begins a math formula causes a |math_delim_group| to
---
> the `\.\$' that begins a math formula causes a |math_shift_group| to
4869c5765
< one that starts with \.{\\groupbegin} should end with \.{\\groupend}.
---
> one that starts with \.{\\begingroup} should end with \.{\\endgroup}.
4874,4889c5770,5784
< @d vbox_group=3 {code for `\.{\\vbox}\grp'}
< @d vtop_group=4 {code for `\.{\\vtop}\grp'}
< @d true_group=5 {code for `\.{\\if...}\grp'}
< @d false_group=6 {code for `\.{\\else}\grp'}
< @d case_group=7 {code for `\.{\\case}\grp'}
< @d align_group=8 {code for `\.{\\halign}\grp', `\.{\\valign}\grp'}
< @d no_align_group=9 {code for `\.{\\noalign}\grp'}
< @d output_group=10 {code for output routine}
< @d math_group=11 {code for, e.g, `\.{\char'176}\grp'}
< @d disc_group=12 {code for `\.{\\discretionary}\grp\grp\grp'}
< @d insert_group=13 {code for `\.{\\insert}\grp', `\.{\\vadjust}\grp'}
< @d vcenter_group=14 {code for `\.{\\vcenter}\grp'}
< @d semi_simple_group=15 {code for `\.{\\groupbegin...\\groupend}'}
< @d math_delim_group=16 {code for `\.{\$...\$}'}
< @d math_left_group=17 {code for `\.{\\left...\\right}'}
< @d max_group_code=17
---
> @d adjusted_hbox_group=3 {code for `\.{\\hbox}\grp' in vertical mode}
> @d vbox_group=4 {code for `\.{\\vbox}\grp'}
> @d vtop_group=5 {code for `\.{\\vtop}\grp'}
> @d align_group=6 {code for `\.{\\halign}\grp', `\.{\\valign}\grp'}
> @d no_align_group=7 {code for `\.{\\noalign}\grp'}
> @d output_group=8 {code for output routine}
> @d math_group=9 {code for, e.g., `\.{\char'136}\grp'}
> @d disc_group=10 {code for `\.{\\discretionary}\grp\grp\grp'}
> @d insert_group=11 {code for `\.{\\insert}\grp', `\.{\\vadjust}\grp'}
> @d vcenter_group=12 {code for `\.{\\vcenter}\grp'}
> @d math_choice_group=13 {code for `\.{\\mathchoice}\grp\grp\grp\grp'}
> @d semi_simple_group=14 {code for `\.{\\begingroup...\\endgroup}'}
> @d math_shift_group=15 {code for `\.{\$...\$}'}
> @d math_left_group=16 {code for `\.{\\left...\\right}'}
> @d max_group_code=16
4892c5787
< @!group_code=0..max_group_code; {|save_index| for a level boundary}
---
> @!group_code=0..max_group_code; {|save_level| for a level boundary}
4896,4898c5791,5792
< topmost |level_boundary| word. And |cur_level| is the current depth
< of nesting, not counting |true_group| and |false_group| and |case_group|
< levels. The routines are designed to preserve the condition that no entry
---
> topmost |level_boundary| word. And |cur_level| is the current depth of
> nesting. The routines are designed to preserve the condition that no entry
4901c5795
< @ @<Globals...@>=
---
> @ @<Glob...@>=
4915,4916c5809,5810
< save_ptr←0; cur_level←level_one; cur_group←bottom_level; cur_boundary←0;
< max_save_stack←0;
---
> save_ptr:=0; cur_level:=level_one; cur_group:=bottom_level; cur_boundary:=0;
> max_save_stack:=0;
4923,4925c5817,5820
< begin max_save_stack←save_ptr;
< if max_save_stack>save_size-6 then overflow("save size",save_size);
< end
---
> begin max_save_stack:=save_ptr;
> if max_save_stack>save_size-6 then overflow("save size",save_size);
> @:TeX capacity exceeded save size}{\quad save size@>
> end
4931c5826
< In some cases a few integer-valued items are placed onto the
---
> In some cases integer-valued items are placed onto the
4946,4947c5841,5842
< save_type(save_ptr)←level_boundary; save_level(save_ptr)←cur_group;
< save_index(save_ptr)←cur_boundary;
---
> save_type(save_ptr):=level_boundary; save_level(save_ptr):=cur_group;
> save_index(save_ptr):=cur_boundary;
4949,4951c5844,5847
< 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;
---
> @:TeX capacity exceeded grouping levels}{\quad 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;
4956c5852
< to keep in mind that reference counts in |m| include references from
---
> to keep in mind that reference counts in |mem| include references from
4957a5854
> @^reference counts@>
4964,4966c5861,5863
< shape_ref: begin q←equiv_field(w); {we need to free a \.{\\parshape} block}
< if q≠null then free_node(q,link(q)+link(q)+1);
< end;
---
> shape_ref: begin q:=equiv_field(w); {we need to free a \.{\\parshape} block}
> if q<>null then free_node(q,info(q)+info(q)+1);
> end; {such a block is |2n+1| words long, where |n=info(q)|}
4972c5869
< @ To save a value of |eqtb[p]| that was defined at level |l|, we
---
> @ To save a value of |eqtb[p]| that was established at level |l|, we
4977,4981c5874,5878
< if l=level_zero then save_type(save_ptr)←restore_zero
< else begin save_stack[save_ptr]←eqtb[p]; incr(save_ptr);
< save_type(save_ptr)←restore_old_value;
< end;
< save_level(save_ptr)←l; save_index(save_ptr)←p; incr(save_ptr);
---
> if l=level_zero then save_type(save_ptr):=restore_zero
> else begin save_stack[save_ptr]:=eqtb[p]; incr(save_ptr);
> save_type(save_ptr):=restore_old_value;
> end;
> save_level(save_ptr):=l; save_index(save_ptr):=p; incr(save_ptr);
4993c5890
< {new data for |eqtb|}
---
> {new data for |eqtb|}
4996c5893
< eq_level(p)←cur_level; eq_type(p)←t; equiv(p)←e;
---
> eq_level(p):=cur_level; eq_type(p):=t; equiv(p):=e;
5000c5897
< |eqtb| is called |eq_word_define|. Since |xeq_level[p]≥level_one| for all
---
> |eqtb| is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all
5004,5007c5901,5904
< begin if xeq_level[p]≠cur_level then
< begin eq_save(p,xeq_level[p]); xeq_level[p]←cur_level;
< end;
< eqtb[p].int←w;
---
> begin if xeq_level[p]<>cur_level then
> begin eq_save(p,xeq_level[p]); xeq_level[p]:=cur_level;
> end;
> eqtb[p].int:=w;
5012c5909
< Global defi\-ni\-tions are done in almost the same way, but there is no need
---
> Global definitions are done in almost the same way, but there is no need
5016c5913
< {global |eq_define|}
---
> {global |eq_define|}
5018c5915
< eq_level(p)←level_one; eq_type(p)←t; equiv(p)←e;
---
> eq_level(p):=level_one; eq_type(p):=t; equiv(p):=e;
5022c5919
< begin eqtb[p].int←w; xeq_level[p]←level_one;
---
> begin eqtb[p].int:=w; xeq_level[p]:=level_one;
5025,5027c5922,5930
< @ The |unsave| routine goes the other way, taking items off of |save_stack|;
< it is not used when |cur_group| is |true_group|, |false_group|, or |case_group|,
< since those groups are ``transparent'' with respect to their environment.
---
> @ Subroutine |save_for_after| puts a token on the stack for save-keeping.
>
> @p procedure save_for_after(@!t:halfword);
> begin if cur_level>level_one then
> begin check_full_save_stack;
> save_type(save_ptr):=insert_token; save_level(save_ptr):=level_zero;
> save_index(save_ptr):=t; incr(save_ptr);
> end;
> end;
5028a5932
> @ The |unsave| routine goes the other way, taking items off of |save_stack|.
5032c5936,5938
< @p procedure unsave; {pops the top level off the save stack}
---
> @p@t\4@>@<Declare the procedure called |restore_trace|@>@;@/
> procedure@?back_input; forward; @t\2@>
> procedure unsave; {pops the top level off the save stack}
5035a5942
> @!t:halfword; {saved value of |cur_tok|}
5037,5039c5944,5946
< begin decr(cur_level);
< @<Clear off top level from |save_stack|@>;
< end
---
> begin decr(cur_level);
> @<Clear off top level from |save_stack|@>;
> end
5041c5948
< @:confusion curlevel}{\quad curlevel@>
---
> @:this can't happen curlevel}{\quad curlevel@>
5046,5055c5953,5965
< if save_type(save_ptr)=level_boundary then goto done;
< p←save_index(save_ptr);
< if save_type(save_ptr)=restore_old_value then
< begin l←save_level(save_ptr); decr(save_ptr);
< end
< else save_stack[save_ptr]←eqtb[undefined_control_sequence];
< @<Store \(s)|save_stack[save_ptr]| in |eqtb[p]|, unless
< |eqtb[p]| holds a global value@>;
< end;
< done: cur_group←save_level(save_ptr); cur_boundary←save_index(save_ptr)
---
> if save_type(save_ptr)=level_boundary then goto done;
> p:=save_index(save_ptr);
> if save_type(save_ptr)=insert_token then
> @<Insert token |p| into \TeX's input@>
> else begin if save_type(save_ptr)=restore_old_value then
> begin l:=save_level(save_ptr); decr(save_ptr);
> end
> else save_stack[save_ptr]:=eqtb[undefined_control_sequence];
> @<Store \(s)|save_stack[save_ptr]| in |eqtb[p]|, unless
> |eqtb[p]| holds a global value@>;
> end;
> end;
> done: cur_group:=save_level(save_ptr); cur_boundary:=save_index(save_ptr)
5065,5090c5975,6018
< if eq_level(p)=level_one then
< eq_destroy(save_stack[save_ptr]) {destroy the saved value}
< else begin eq_destroy(eqtb[p]); {destroy the current value}
< eqtb[p]←save_stack[save_ptr]; {restore the saved value}
< end
< else if xeq_level[p]≠level_one then
< begin eqtb[p]←save_stack[save_ptr]; xeq_level[p]←l;
< end
<
< @ When a |true_group| or one of its relatives comes to an end, we merely
< want to delete the top |level_boundary| word, restoring the former
< values of |cur_group| and |cur_boundary|. The |leave_transparent_group|
< routine does this.
<
< @p procedure leave_transparent_group;
< var j,@!k:0..save_size; {indices into |save_stack|}
< begin j←save_index(cur_boundary); cur_group←save_level(cur_boundary);
< if save_ptr>1 then
< for k←cur_boundary to save_ptr-2 do save_stack[k]←save_stack[k+1];
< decr(save_ptr); cur_boundary←j;
< end;
<
< @ The magnification parameter should not be varied during a \TeX\ job, since
< a single magnification is applied to an entire run. The global variable
< |mag_set| is set to the current magnification whenever it becomes necessary
< to ``freeze'' the magnification at a particular value.
---
> if eq_level(p)=level_one then
> begin eq_destroy(save_stack[save_ptr]); {destroy the saved value}
> @!stat if tracing_restores>0 then restore_trace(p,"retaining");@+tats@;@/
> end
> else begin eq_destroy(eqtb[p]); {destroy the current value}
> eqtb[p]:=save_stack[save_ptr]; {restore the saved value}
> @!stat if tracing_restores>0 then restore_trace(p,"restoring");@+tats@;@/
> end
> else if xeq_level[p]<>level_one then
> begin eqtb[p]:=save_stack[save_ptr]; xeq_level[p]:=l;
> @!stat if tracing_restores>0 then restore_trace(p,"restoring");@+tats@;@/
> end
> else begin
> @!stat if tracing_restores>0 then restore_trace(p,"retaining");@+tats@;@/
> end
>
> @ @<Declare the procedure called |restore_trace|@>=
> @!stat procedure restore_trace(@!p:pointer;@!s:str_number);
> {|eqtb[p]| has just been restored or retained}
> begin begin_diagnostic; print_char("{"); print(s); print_char(" ");
> show_eqtb(p); print_char("}");
> end_diagnostic(false);
> end;
> tats
>
> @ 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.
> @^dirty \PASCAL@>
>
> @<Search |save_stack| for equivalents that point to |p|@>=
> if save_ptr>0 then for q:=0 to save_ptr-1 do
> begin if equiv_field(save_stack[q])=p then
> begin print_nl("SAVE("); print_int(q); print_char(")");
> end;
> end
>
> @ 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 \TeX\ job, since a single magnification is applied to an
> entire run. The global variable |mag_set| is set to the current magnification
> whenever it becomes necessary to ``freeze'' it at a particular value.
5096c6024
< mag_set←0;
---
> mag_set:=0;
5102,5103c6030,6031
< begin if (mag_set>0)∧(mag≠mag_set) then
< begin print_nl("! Incompatible magnification ("); print_int(mag);
---
> begin if (mag_set>0)and(mag<>mag_set) then
> begin print_err("Incompatible magnification ("); print_int(mag);
5105,5112c6033,6040
< print("); 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|}
< end;
< if (mag≤0)∨(mag>32768) then
< begin print_nl("! Illegal magnification has been changed to 1000");@/
---
> 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|}
> end;
> if (mag<=0)or(mag>32768) then
> begin print_err("Illegal magnification has been changed to 1000");@/
5114,5117c6042,6045
< help1("The magnification ratio must be between 1 and 32768.");
< int_error(mag); geq_word_define(int_base+mag_code,1000);
< end;
< mag_set←mag;
---
> help1("The magnification ratio must be between 1 and 32768.");
> int_error(mag); geq_word_define(int_base+mag_code,1000);
> end;
> mag_set:=mag;
5118a6047
>
5122,5128c6051,6057
< represented internally in one of two ways: (1)@@A character whose ascii code
< number is |c| and whose command code is |m| is represented as the
< number $2^8m+c$; the command code is in the range
< |1≤m≤14|. (2)@@A control sequence whose |eqtb| address is |p| is
< represented as the number |cs_token_flag+p|. Here |cs_token_flag=@t$2^{12}$@>|
< is larger than $2^8m+c$, yet it is small enough that |cs_token_flag+p<
< max_halfword|; thus, a token fits comfortably in a halfword.
---
> represented internally in one of two ways: (1)~A character whose ASCII
> code number is |c| and whose command code is |m| is represented as the
> number $2^8m+c$; the command code is in the range |1<=m<=14|. (2)~A control
> sequence whose |eqtb| address is |p| is represented as the number
> |cs_token_flag+p|. Here |cs_token_flag=@t$2^{12}-1$@>| is larger than
> $2^8m+c$, yet it is small enough that |cs_token_flag+p< max_halfword|;
> thus, a token fits comfortably in a halfword.
5132,5133c6061,6062
< we have |left_brace_limit≤t<right_brace_limit|; and it represents a |match| or
< |end_match| command if and only if |match_token≤t≤end_match_token|.
---
> we have |left_brace_limit<=t<right_brace_limit|; and it represents a |match| or
> |end_match| command if and only if |match_token<=t<=end_match_token|.
5137,5138c6066,6067
< @d cs_token_flag==@'10000 {amount added to the |eqtb| location in a
< token that stands for a control sequence}
---
> @d cs_token_flag==@'7777 {amount added to the |eqtb| location in a
> token that stands for a control sequence; is a multiple of~256, less~1}
5143c6072,6073
< @d math_delim_token=@'1400 {$2^8\cdot|math_delim|$}
---
> @d math_shift_token=@'1400 {$2^8\cdot|math_shift|$}
> @d tab_token=@'2000 {$2^8\cdot|tab_mark|$}
5145d6074
< @d endv_token=@'4400 {$2^8\cdot|endv|$}
5153c6082
< if cs_token_flag+undefined_control_sequence>max_halfword then bad←21;
---
> if cs_token_flag+undefined_control_sequence>max_halfword then bad:=21;
5157,5158c6086,6088
< definitions, marks, and \.{\\send} texts are kept in \TeX's memory in the
< form of token lists, preceded by a node that has a reference count in its
---
> definitions, marks, \.{\\write} texts, and a few other things
> are remembered by \TeX\ in the form
> of token lists, usually preceded by a node with a reference count in its
5180c6110
< |letter|\,\.{\\b}, |end_match|,\cr
---
> \.{\\b}, |end_match|,\cr
5185c6115
< does the param\-eter matching.
---
> does the parameter matching.
5208c6138
< An additional parameter |q| is also given; this parameter is either zero
---
> An additional parameter |q| is also given; this parameter is either null
5210c6140
< takes place that will be explained later. (Basically, |q| is nonzero when
---
> takes place that will be explained later. (Basically, |q| is non-null when
5222c6152
< of printing exceeds a given limit@@|l|. Anomalous entries are printed in the
---
> of printing exceeds a given limit~|l|. Anomalous entries are printed in the
5231,5239c6161,6170
< @!match_chr:ascii_code; {character used in a `|match|'}
< @!n:ascii_code; {the highest parameter number, as an ascii digit}
< begin match_chr←"#"; n←"0"; tally←0;
< while (p≠null) and (tally<l) do
< begin if p=q then @<Do magic computation@>;
< @<Display token |p|, and |return| if there are problems@>;
< p←link(p);
< end;
< if p≠null then print_esc("ETC.");
---
> @!match_chr:ASCII_code; {character used in a `|match|'}
> @!n:ASCII_code; {the highest parameter number, as an ASCII digit}
> begin match_chr:="#"; n:="0"; tally:=0;
> while (p<>null) and (tally<l) do
> begin if p=q then @<Do magic computation@>;
> @<Display token |p|, and |return| if there are problems@>;
> p:=link(p);
> end;
> if p<>null then print_esc("ETC.");
> @.ETC@>
5244,5251c6175,6184
< if (p<hi_mem_base) or (p>mem_end) then
< begin print_esc("CLOBBERED."); return;
< end;
< if info(p)≥cs_token_flag then print_cs(info(p)-cs_token_flag)
< else begin m←info(p) div @'400; c←info(p) mod @'400;
< if (info(p)<0)∨(c>127) then print_esc("BAD.")
< else @<Display the token $(|m|,|c|)$@>;
< end
---
> if (p<hi_mem_min) or (p>mem_end) then
> begin print_esc("CLOBBERED."); return;
> @.CLOBBERED@>
> end;
> if info(p)>=cs_token_flag then print_cs(info(p)-cs_token_flag)
> else begin m:=info(p) div @'400; c:=info(p) mod @'400;
> if info(p)<0 then print_esc("BAD.")
> @.BAD@>
> else @<Display the token $(|m|,|c|)$@>;
> end
5254c6187
< param\-eters by seeing one in a |match| command before it runs into any
---
> parameters by seeing one in a |match| command before it runs into any
5259,5266c6192,6193
< right_brace,math_delim,tab_mark,sup_mark,sub_mark,spacer,
< letter,other_char: print(c);
< left_brace:begin if (link(p)≥hi_mem_base)∧(link(p)≤mem_end) then
< if info(link(p)) div @'400=end_match then
< print(match_chr);
< print(c);
< end;
< endv:print_esc("ENDTEMPLATE.");
---
> left_brace,right_brace,math_shift,tab_mark,sup_mark,sub_mark,spacer,
> letter,other_char: print(c);
5268c6195
< end;
---
> end;
5270,5276c6197,6203
< if c≤9 then print_char(c+"0")
< else begin print_char("!"); return;
< end;
< end;
< match: begin match_chr←c; print(c); incr(n); print_char(n);
< if n>"9" then return;
< end;
---
> if c<=9 then print_char(c+"0")
> else begin print_char("!"); return;
> end;
> end;
> match: begin match_chr:=c; print(c); incr(n); print_char(n);
> if n>"9" then return;
> end;
5277a6205
> @.->@>
5278a6207
> @.BAD@>
5285,5286c6214,6228
< begin if p=null then print("(null)")
< else show_token_list(link(p),null,1000);
---
> begin if p<>null then show_token_list(link(p),null,10000000);
> end;
>
> @ The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in
> symbolic form, including the expansion of a macro or mark.
>
> @p procedure print_meaning;
> begin print_cmd_chr(cur_cmd,cur_chr);
> if cur_cmd>=call then
> begin print_char(":"); print_ln; token_show(cur_chr);
> end
> else if cur_cmd=top_bot_mark then
> begin print_char(":"); print_ln;
> token_show(cur_mark[cur_chr]);
> end;
5287a6230
>
5303,5308c6246,6255
< sets the value of two variables |cur_cmd| and |cur_chr|, representing
< the next input token.
< $$\lpile{\hbox{|cur_cmd| denotes a command code from the long list of codes
< given above;}\cr
< \hbox{|cur_chr| denotes a character code or other modifier of the command
< code.}\cr}$$
---
> sets the value of three variables |cur_cmd|, |cur_chr|, and |cur_cs|,
> representing the next input token.
> $$\vbox{\halign{#\hfil\cr
> \hbox{|cur_cmd| denotes a command code from the long list of codes
> given above;}\cr
> \hbox{|cur_chr| denotes a character code or other modifier of the command
> code;}\cr
> \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr
> \hbox{\qquad if the current token was a control sequence,
> otherwise it's zero.}\cr}}$$
5331,5332c6278,6279
< @<Globals...@>=
< @!cur_cmd : eight_bits; {current command set by |get_next|}
---
> @<Glob...@>=
> @!cur_cmd: eight_bits; {current command set by |get_next|}
5334c6281,6282
< @!cur_tok:halfword; {packed representative of |cur_cmd| and |cur_chr|}
---
> @!cur_cs: pointer; {control sequence found here, zero if none found}
> @!cur_tok: halfword; {packed representative of |cur_cmd| and |cur_chr|}
5347,5348c6295,6296
< @d chr_cmd(#)==begin print(#); print_ascii(chr_code);
< end
---
> @d chr_cmd(#)==begin print(#); print_ASCII(chr_code);
> end
5350c6298,6299
< @p procedure print_cmd_chr(@!cmd:quarterword;@!chr_code:halfword);
---
> @<Declare the procedure called |print_cmd_chr|@>=
> procedure print_cmd_chr(@!cmd:quarterword;@!chr_code:halfword);
5354,5357c6303
< math_delim: chr_cmd("math mode character ");
< tab_mark: if chr_code=128 then print_esc("span") {|span_code=128|}
< else chr_cmd("alignment tab character ");
< car_ret: print_esc("cr");
---
> math_shift: chr_cmd("math shift character ");
5362c6308
< spacer: print("blank space");
---
> spacer: chr_cmd("blank space ");
5368a6315,6326
>
> @ Here is a procedure that displays the current command.
>
> @p procedure show_cur_cmd_chr;
> begin begin_diagnostic; print_nl("{");
> if mode<>shown_mode then
> begin print_mode(mode); print(": "); shown_mode:=mode;
> end;
> print_cmd_chr(cur_cmd,cur_chr); print_char("}");
> end_diagnostic(false);
> end;
>
5369a6328
> This implementation of
5373c6332
< \yskip\hang 1) If there is frequent access to the top entry, and if the
---
> \yskip\hangg 1) If there is frequent access to the top entry, and if the
5380c6339
< \yskip\hang 2) If there is infrequent top access, the entire stack contents
---
> \yskip\hangg 2) If there is infrequent top access, the entire stack contents
5388c6347
< convention@@(1), so it is declared in the following way:
---
> convention~(1), so it is declared in the following way:
5391,5394c6350,6353
< @!in_state_record = record
< @!state_field, @!index_field: quarterword;
< @!start_field,@!loc_field, @!limit_field, @!name_field: halfword;
< end;
---
> @!in_state_record = record
> @!state_field, @!index_field: quarterword;
> @!start_field,@!loc_field, @!limit_field, @!name_field: halfword;
> end;
5396c6355
< @ @<Globals...@>=
---
> @ @<Glob...@>=
5401c6360
< {the ``top'' input state, according to convention (1)}
---
> {the ``top'' input state, according to convention (1)}
5403c6362
< @ We've already defined the special variables |@!loc==cur_input.loc_field|
---
> @ We've already defined the special variable |loc==cur_input.loc_field|
5405c6364
< the |cur_input| are defined in the same way:
---
> |cur_input| are defined in the same way:
5413,5414c6372,6373
< @ Let's look more closely now at the six control variables
< (|state|,@@|index|,@@|start|,@@|loc|,@@|limit|,@@|name|),
---
> @ Let's look more closely now at the control variables
> (|state|,~|index|,~|start|,~|loc|,~|limit|,~|name|),
5419,5420c6378,6379
< levels of the input stack that are not yet completed. We will return to
< the other lines when we are finished with the present input file.
---
> levels of the input stack that are not yet completed. \TeX\ will return to
> the other lines when it is finished with the present input file.
5422c6381
< (Incidentally, on a machine with byte-oriented addressing, it would be
---
> (Incidentally, on a machine with byte-oriented addressing, it might be
5430,5431c6389,6390
< the line has been completely read. Usually |buffer[limit]| is a
< |carriage_return| character, denoting the end of a line, but this is not
---
> the line has been completely read. Usually |buffer[limit]| is the
> |end_line_char|, denoting the end of a line, but this is not
5433c6392
< terminal in response to an error message.
---
> terminal in response to an error message.
5436,5437c6395,6399
< the current file, if we are reading a text file; it is zero if we
< are reading from the terminal.
---
> the current file, if we are reading a text file. It is zero if we
> are reading from the terminal; it is |n+1| if we are reading from
> input stream |n|, where |0<=n<=16|. (Input stream 16 stands for
> an invalid stream number; in such cases the input is actually from
> the terminal, under control of the procedure |read_toks|.)
5463,5464c6425,6426
< stack come from token lists. For example, the instruction `\.{\\input paper}'
< might occur in a token list.
---
> stack may come from token lists. For example, the instruction `\.{\\input
> paper}' might occur in a token list.
5466c6428
< The variable |in_open| is equal to the |index|
---
> The global variable |in_open| is equal to the |index|
5471,5474c6433,6436
< If we are not currently reading from the terminal, we are reading from the
< file variable |input_file[index]|. We use the notation |terminal_input| as
< a convenient abbreviation for |name=0|, and |cur_file| as an abbreviation
< for |input_file[index]|.
---
> If we are not currently reading from the terminal, or from an input
> stream, we are reading from the file variable |input_file[index]|. We use
> the notation |terminal_input| as a convenient abbreviation for |name=0|,
> and |cur_file| as an abbreviation for |input_file[index]|.
5480c6442,6444
< file has been read.
---
> file has been read. Line numbers should never be negative, since the
> negative of the current line number is used to identify the user's output
> routine in the |mode_line| field of the semantic nest entries.
5486c6450
< `\unskip|@!page_stack:array[0..max_in_open] of integer|'
---
> `\ignorespaces|@!page_stack:array[1..max_in_open] of integer|\unskip'
5493c6457
< @<Globals...@>=
---
> @<Glob...@>=
5494a6459
> @!open_parens : 0..max_in_open; {the number of open text files}
5497c6462
< @!line_stack : array[0..max_in_open] of integer;
---
> @!line_stack : array[1..max_in_open] of integer;
5505c6470
< when a subfile ends. This variable has five possible values:
---
> when a subfile ends. This variable has six possible values:
5520a6486,6488
> \yskip\hang|absorbing|, means that a subfile shouldn't end now because we are
> reading a balanced token list for \.{\\message}, \.{\\write}, etc.
>
5529a6498
> @d absorbing=5 {|scanner_status| when reading a balanced text}
5531,5532c6500,6501
< @<Globals...@>=
< @!scanner_status : normal..aligning; {can a subfile end now?}
---
> @<Glob...@>=
> @!scanner_status : normal..absorbing; {can a subfile end now?}
5533a6503
> @!def_ref : pointer; {reference count of token list being defined}
5542,5552c6512,6525
< begin print_nl("Runaway ");
< case scanner_status of
< defining: begin print("definition?"); p←temp_head;
< end;
< matching: begin print("argument?"); p←temp_head;
< end;
< aligning: begin print("preamble?"); p←hold_head;
< end;
< end; {there are no other cases}
< print_ln; show_token_list(link(p),null,error_line-10);
< end;
---
> begin print_nl("Runaway ");
> @.Runaway...@>
> case scanner_status of
> defining: begin print("definition"); p:=def_ref;
> end;
> matching: begin print("argument"); p:=temp_head;
> end;
> aligning: begin print("preamble"); p:=hold_head;
> end;
> absorbing: begin print("text"); p:=def_ref;
> end;
> end; {there are no other cases}
> print_char("?");print_ln; show_token_list(link(p),null,error_line-10);
> end;
5585c6558
< \hang|u_template|, if the \<u↓j> part of an alignment
---
> \hang|u_template|, if the \<u_j> part of an alignment
5588c6561
< \hang|v_template|, if the \<v↓j> part of an alignment
---
> \hang|v_template|, if the \<v_j> part of an alignment
5590a6564,6566
> \hang|backed_up|, if the token list being scanned has been inserted as
> `to be read again'.
>
5592,5593c6568
< `to be read again' or as the text expansion of a \.{\\count} or similar
< variable;
---
> the text expansion of a \.{\\count} or similar variable;
5601c6576,6582
< \hang|mark_text|, if the text of a \.{\\mark} is being scanned.
---
> \hang|every_math_text|, if the text of \.{\\everymath} is being scanned;
>
> \hang|every_display_text|, if the text of \.{\\everydisplay} is being scanned;
>
> \hang|every_hbox_text|, if the text of \.{\\everyhbox} is being scanned;
>
> \hang|every_vbox_text|, if the text of \.{\\everyvbox} is being scanned;
5603c6584,6590
< \hang|send_text|, if the text of a \.{\\send} is being scanned.
---
> \hang|every_job_text|, if the text of \.{\\everyjob} is being scanned;
>
> \hang|every_cr_text|, if the text of \.{\\everycr} is being scanned;
>
> \hang|mark_text|, if the text of a \.{\\mark} is being scanned;
>
> \hang|write_text|, if the text of a \.{\\write} is being scanned.
5606,5607c6593,6596
< The token list begins with a reference count if and only if |token_type≥
< macro|.
---
> The codes for |output_text|, |every_par_text|, etc., are equal to a constant
> plus the corresponding codes for token list parameters |output_routine_loc|,
> |every_par_loc|, etc. The token list begins with a reference count if and
> only if |token_type>=macro|.
5614,5621c6603,6617
< @d u_template=1 {|token_type| code for \<u↓j> template}
< @d v_template=2 {|token_type| code for \<v↓j> template}
< @d inserted=3 {|token_type| code for short inserted texts}
< @d macro=4 {|token_type| code for defined control sequences}
< @d output_text=5 {|token_type| code for output routines}
< @d every_par_text=6 {|token_type| code for \.{\\everypar}}
< @d mark_text=7 {|token_type| code for \.{\\topmark}, etc.}
< @d send_text=8 {|token_type| code for \.{\\send}}
---
> @d u_template=1 {|token_type| code for \<u_j> template}
> @d v_template=2 {|token_type| code for \<v_j> template}
> @d backed_up=3 {|token_type| code for text to be reread}
> @d inserted=4 {|token_type| code for inserted texts}
> @d macro=5 {|token_type| code for defined control sequences}
> @d output_text=6 {|token_type| code for output routines}
> @d every_par_text=7 {|token_type| code for \.{\\everypar}}
> @d every_math_text=8 {|token_type| code for \.{\\everymath}}
> @d every_display_text=9 {|token_type| code for \.{\\everydisplay}}
> @d every_hbox_text=10 {|token_type| code for \.{\\everyhbox}}
> @d every_vbox_text=11 {|token_type| code for \.{\\everyvbox}}
> @d every_job_text=12 {|token_type| code for \.{\\everyjob}}
> @d every_cr_text=13 {|token_type| code for \.{\\everycr}}
> @d mark_text=14 {|token_type| code for \.{\\topmark}, etc.}
> @d write_text=15 {|token_type| code for \.{\\write}}
5628c6624
< @<Globals...@>=
---
> @<Glob...@>=
5630c6626
< {token list pointers for parameters}
---
> {token list pointers for parameters}
5633c6629
< {largest value of |param_ptr|, will be |≤param_size+9|}
---
> {largest value of |param_ptr|, will be |<=param_size+9|}
5638,5642c6634,6638
< \<v↓j> template text in the scanner. This magic is accomplished by an
< |align_state| variable that is increased by@@1 when a `\.{\char'173}' is
< scanned and decreased by@@1 when a `\.{\char'175}' is scanned. The |align_state|
< is nonzero during the \<u↓j> template, after which it is set to zero; the
< \<v↓j> template begins when a tab mark or \.{\\cr} occurs at a time that
---
> \<v_j> template text in the scanner. This magic is accomplished by an
> |align_state| variable that is increased by~1 when a `\.{\char'173}' is
> scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state|
> is nonzero during the \<u_j> template, after which it is set to zero; the
> \<v_j> template begins when a tab mark or \.{\\cr} occurs at a time that
5645c6641
< @<Globals...@>=
---
> @<Glob...@>=
5648c6644
< @ Thus, the ``current input state'' can be very complicated indeed, since there
---
> @ Thus, the ``current input state'' can be very complicated indeed; there
5663,5664c6659,6660
< at most |error_line|. Input levels whose |token_type| is `|inserted|'
< are shown only if they have not been fully read.
---
> at most |error_line|. Non-current input levels whose |token_type| is
> `|backed_up|' are shown only if they have not been fully read.
5668a6665,6666
> @!nn:integer; {number of contexts shown so far, less one}
> @!bottom_line:boolean; {have we reached the final context to be shown?}
5670,5678c6668,6682
< begin base_ptr←input_ptr; input_stack[base_ptr]←cur_input;
< {store current state}
< loop@+begin cur_input←input_stack[base_ptr]; {enter into the context}
< @<Display the current context@>;
< if (state≠token_list) then
< if (not terminal_input) or (base_ptr=0) then goto done;
< decr(base_ptr);
< end;
< done: cur_input←input_stack[input_ptr]; {restore original state}
---
> begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
> {store current state}
> nn:=-1; bottom_line:=false;
> loop@+begin cur_input:=input_stack[base_ptr]; {enter into the context}
> if (state<>token_list) then
> if (name>17) or (base_ptr=0) then bottom_line:=true;
> if (base_ptr=input_ptr)or bottom_line or(nn<error_context_lines) then
> @<Display the current context@>
> else if nn=error_context_lines then
> begin print_nl("..."); incr(nn); {omitted if |error_context_lines<0|}
> end;
> if bottom_line then goto done;
> decr(base_ptr);
> end;
> done: cur_input:=input_stack[input_ptr]; {restore original state}
5682,5695c6686,6702
< if (state≠token_list) or (token_type≠inserted) or (loc≠null) then
< {we omit inserted token lists that have already been read}
< begin tally←0; {get ready to count characters}
< old_setting←selector;
< if state≠token_list then
< begin @<Print location of current line@>;
< @<Pseudoprint the line@>;
< end
< else begin @<Print type of token list@>;
< @<Pseudoprint the token list@>;
< end;
< selector←old_setting; {stop pseudoprinting}
< @<Print two lines using the tricky pseudoprinted information@>;
< end
---
> begin if (base_ptr=input_ptr) or (state<>token_list) or
> (token_type<>backed_up) or (loc<>null) then
> {we omit backed-up token lists that have already been read}
> begin tally:=0; {get ready to count characters}
> old_setting:=selector;
> if state<>token_list then
> begin @<Print location of current line@>;
> @<Pseudoprint the line@>;
> end
> else begin @<Print type of token list@>;
> @<Pseudoprint the token list@>;
> end;
> selector:=old_setting; {stop pseudoprinting}
> @<Print two lines using the tricky pseudoprinted information@>;
> incr(nn);
> end;
> end
5703,5706c6710,6719
< if terminal_input then
< if base_ptr=0 then print_nl("<*>") else print_nl("<**>")
< else begin print_nl("l."); print_int(line);
< end;
---
> if name<=17 then
> if terminal_input then
> if base_ptr=0 then print_nl("<*>") else print_nl("<insert> ")
> else begin print_nl("<read ");
> if name=17 then print_char("*")@+else print_int(name-1);
> @.*\relax@>
> print_char(">");
> end
> else begin print_nl("l."); print_int(line);
> end;
5713c6726,6728
< inserted: print_nl("<to be read again> ");
---
> backed_up: if loc=null then print_nl("<recently read> ")
> else print_nl("<to be read again> ");
> inserted: print_nl("<inserted text> ");
5715c6730
< end;
---
> end;
5717a6733,6738
> every_math_text: print_nl("<everymath> ");
> every_display_text: print_nl("<everydisplay> ");
> every_hbox_text: print_nl("<everyhbox> ");
> every_vbox_text: print_nl("<everyvbox> ");
> every_job_text: print_nl("<everyjob> ");
> every_cr_text: print_nl("<everycr> ");
5719c6740
< send_text: print_nl("<send> ");
---
> write_text: print_nl("<write> ");
5728c6749
< that stores char\-acters into a buffer of length |error_line|, where character
---
> that stores characters into a buffer of length |error_line|, where character
5731c6752
< |tally←0| and |trick_count←1000000|; then when we reach the
---
> |tally:=0| and |trick_count:=1000000|; then when we reach the
5733c6754
< set |first_count←tally| and |trick_count←@tmax@>(error_line,
---
> set |first_count:=tally| and |trick_count:=@tmax@>(error_line,
5742c6763
< gathered for line@@2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k≤h|,
---
> gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|,
5744,5745c6765,6766
< descriptive information on line@@1, and set |n←l+k|; here |n| is the
< length of line@@1. If $l+k>h$, some cropping is necessary, so we set |n←h|
---
> descriptive information on line~1, and set |n:=l+k|; here |n| is the
> length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h|
5749c6770
< second line consists of |n|@@spaces followed by |trick_buf[k..(k+m-1)]|,
---
> second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|,
5766,5768c6787,6789
< begin l←tally; tally←0; selector←pseudo;
< trick_count←1000000;
< end
---
> begin l:=tally; tally:=0; selector:=pseudo;
> trick_count:=1000000;
> end
5770,5773c6791,6794
< begin first_count←tally;
< trick_count←tally+1+error_line-half_error_line;
< if trick_count<error_line then trick_count←error_line;
< end
---
> begin first_count:=tally;
> trick_count:=tally+1+error_line-half_error_line;
> if trick_count<error_line then trick_count:=error_line;
> end
5779,5788c6800,6809
< {|set_trick_count| must be performed}
< if tally<trick_count then m←tally-first_count
< else m←trick_count-first_count; {context on line 2}
< if l+first_count≤half_error_line then
< begin p←0; n←l+first_count;
< end
< else begin print("..."); p←l+first_count-half_error_line+3;
< n←half_error_line;
< end;
< for q←p to first_count-1 do print_char(trick_buf[q mod error_line]);
---
> {|set_trick_count| must be performed}
> if tally<trick_count then m:=tally-first_count
> else m:=trick_count-first_count; {context on line 2}
> if l+first_count<=half_error_line then
> begin p:=0; n:=l+first_count;
> end
> else begin print("..."); p:=l+first_count-half_error_line+3;
> n:=half_error_line;
> end;
> for q:=p to first_count-1 do print_char(trick_buf[q mod error_line]);
5790,5792c6811,6813
< for q←1 to n do print_char(" "); {print |n| spaces to begin line@@2}
< if m+n≤error_line then p←first_count+m else p←first_count+(error_line-n-3);
< for q←first_count to p-1 do print_char(trick_buf[q mod error_line]);
---
> for q:=1 to n do print_char(" "); {print |n| spaces to begin line~2}
> if m+n<=error_line then p:=first_count+m else p:=first_count+(error_line-n-3);
> for q:=first_count to p-1 do print_char(trick_buf[q mod error_line]);
5801,5806c6822,6827
< if buffer[limit]=carriage_return then j←limit
< else j←limit+1; {determine the effective end of the line}
< if j>0 then for i←start to j-1 do
< begin if i=loc then set_trick_count;
< print(buffer[i]);
< end
---
> if buffer[limit]=end_line_char then j:=limit
> else j:=limit+1; {determine the effective end of the line}
> if j>0 then for i:=start to j-1 do
> begin if i=loc then set_trick_count;
> print(buffer[i]);
> end
5814c6835
< token beginning line@@2 is about to be shown:
---
> token beginning line~2 is about to be shown:
5816a6838
>
5823,5830c6845,6853
< @p procedure push_input; {enter a new input level, save the old}
< begin if input_ptr>max_in_stack then
< begin max_in_stack←input_ptr;
< if input_ptr=stack_size then overflow("input stack size",stack_size);
< end;
< input_stack[input_ptr]←cur_input; {stack the record}
< incr(input_ptr);
< end;
---
> @d push_input==@t@> {enter a new input level, save the old}
> begin if input_ptr>max_in_stack then
> begin max_in_stack:=input_ptr;
> if input_ptr=stack_size then overflow("input stack size",stack_size);
> @:TeX capacity exceeded input stack size}{\quad input stack size@>
> end;
> input_stack[input_ptr]:=cur_input; {stack the record}
> incr(input_ptr);
> end
5834,5836c6857,6859
< @p procedure pop_input; {leave an input level, re-enter the old}
< begin decr(input_ptr); cur_input←input_stack[input_ptr];
< end;
---
> @d pop_input==@t@> {leave an input level, re-enter the old}
> begin decr(input_ptr); cur_input:=input_stack[input_ptr];
> end
5841a6865
> @d back_list(#)==begin_token_list(#,backed_up) {backs up a simple token list}
5845,5850c6869,6885
< begin push_input; state←token_list; start←p; token_type←t;
< if t≥macro then {the token list starts with a reference count}
< begin add_token_ref(p);
< if t=macro then param_start←param_ptr@+else loc←link(p);
< end
< else loc←p;
---
> begin push_input; state:=token_list; start:=p; token_type:=t;
> if t>=macro then {the token list starts with a reference count}
> begin add_token_ref(p);
> if t=macro then param_start:=param_ptr
> else begin loc:=link(p);
> if tracing_macros>1 then
> begin begin_diagnostic; print_nl("");
> case t of
> mark_text:print_esc("mark");
> write_text:print_esc("write");
> othercases print_cmd_chr(assign_toks,t-output_text+output_routine_loc)
> endcases;@/
> print("->"); token_show(p); end_diagnostic(false);
> end;
> end;
> end
> else loc:=p;
5854c6889,6891
< should be done as we leave that level of input.
---
> 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.
> @^inner loop@>
5857,5867c6894,6907
< begin if token_type≥inserted then {token list to be deleted}
< begin if token_type=inserted then flush_list(start)
< else begin delete_token_ref(start); {update reference count}
< if token_type=macro then {parameters must be flushed}
< while param_ptr>param_start do
< begin decr(param_ptr);
< flush_list(param_stack[param_ptr]);
< end;
< end;
< end
< else if token_type=u_template then align_state←0;
---
> begin if token_type>=backed_up then {token list to be deleted}
> begin if token_type<=inserted then flush_list(start)
> else begin delete_token_ref(start); {update reference count}
> if token_type=macro then {parameters must be flushed}
> while param_ptr>param_start do
> begin decr(param_ptr);
> flush_list(param_stack[param_ptr]);
> end;
> end;
> end
> else if token_type=u_template then
> if align_state>500000 then align_state:=0
> else fatal_error("(interwoven alignment preambles are not allowed)");
> @.interwoven alignment preambles...@>
5872c6912
< @ Sometimes \TeX\ has read too far and it wants to ``unscan'' what it has
---
> @ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has
5876c6916,6918
< replaced.
---
> replaced. Some applications of \TeX\ use this procedure a lot,
> so it has been slightly optimized for speed.
> @^inner loop@>
5880c6922,6924
< begin p←get_avail; info(p)←cur_tok;
---
> begin while (state=token_list)and(loc=null)and(token_type<>v_template) do
> end_token_list; {conserve stack space}
> p:=get_avail; info(p):=cur_tok;
5882,5884c6926,6929
< if cur_tok<left_brace_limit then decr(align_state)
< else incr(align_state);
< ins_list(p);
---
> if cur_tok<left_brace_limit then 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}
5886a6932,6935
> @ @<Insert token |p| into \TeX's input@>=
> begin t:=cur_tok; cur_tok:=p; back_input; cur_tok:=t;
> end
>
5889c6938,6939
< requires that |cur_tok| has been set.
---
> 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.
5892c6942,6947
< begin back_input; error;
---
> begin OK_to_interrupt:=false; back_input; OK_to_interrupt:=true; error;
> end;
> @#
> procedure ins_error; {back up one inserted token and call |error|}
> begin OK_to_interrupt:=false; back_input; token_type:=inserted;
> OK_to_interrupt:=true; error;
5902a6958
> @:TeX capacity exceeded text input levels}{\quad text input levels@>
5904,5906c6960,6963
< incr(in_open); push_input; index←in_open;
< line_stack[index]←line; start←first; state←mid_line;
< name←0; {|terminal_input| is now |true|}
---
> @:TeX capacity exceeded buffer size}{\quad buffer 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|}
5913,5914c6970,6971
< begin first←start; line←line_stack[index];
< if not terminal_input then a_close(cur_file); {forget it}
---
> begin first:=start; line:=line_stack[index];
> if name>17 then a_close(cur_file); {forget it}
5923,5924c6980,6981
< begin while (state≠token_list)∧ terminal_input ∧@|
< (input_ptr>0)∧(loc>limit) do end_file_reading;
---
> begin while (state<>token_list)and terminal_input and@|
> (input_ptr>0)and(loc>limit) do end_file_reading;
5932,5937c6989,6996
< begin input_ptr←0; max_in_stack←0;
< in_open←0; max_buf_stack←0;
< param_ptr←0; max_param_stack←0;
< scanner_status←normal; first←0;
< state←new_line; start←0; index←0; line←0; name←0;
< align_state←1000000;@/
---
> begin input_ptr:=0; max_in_stack:=0;
> in_open:=0; open_parens:=0; max_buf_stack:=0;
> param_ptr:=0; max_param_stack:=0;
> first:=buf_size; repeat buffer[first]:=0; decr(first); until first=0;
> 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;@/
5939,5940c6998
< {|init_terminal| sets |loc| and |last|}
< limit←last; first←last+1;
---
> limit:=last; first:=last+1; {|init_terminal| has set |loc| and |last|}
5941a7000
>
5944,5947c7003,7007
< we shall develop in the next few paragraphs. Perhaps we shouldn't actually
< call it the ``heart,'' however, because it really acts as \TeX's eyes and
< mouth, reading the source files and gobbling them up. And it also helps
< \TeX\ to regurgitate stored token lists that are to be processed again.
---
> we shall develop in the next few sections of the program. Perhaps we
> shouldn't actually call it the ``heart,'' however, because it really acts
> as \TeX's eyes and mouth, reading the source files and gobbling them up.
> And it also helps \TeX\ to regurgitate stored token lists that are to be
> processed again.
5953c7013
< sequence is stored in |cs_ptr|; otherwise |cs_ptr| is set to zero.
---
> sequence is stored in |cur_cs|; otherwise |cur_cs| is set to zero.
5956c7016
< because of all the cases that need to be handled, as explained above.
---
> because of all the cases that need to be handled.
5958a7019,7023
> When |get_next| is asked to get the next token of a \.{\\read} line,
> it sets |cur_cmd=cur_chr=cur_cs=0| in the case that no more tokens
> appear on that line. (There might not be any tokens at all, if the
> |end_line_char| has |ignore| as its catcode.)
>
5961c7026
< to the appearance of \.{\\par}; we must set |cs_ptr←par_loc|
---
> to the appearance of \.{\\par}; we must set |cur_cs:=par_loc|
5964c7029
< @<Globals...@>=
---
> @<Glob...@>=
5966c7031
< @!par_token:halfword; {token representing '\.{\\par}'}
---
> @!par_token:halfword; {token representing `\.{\\par}'}
5969c7034
< primitive("par",par_end,0); par_loc←cur_val; par_token←cs_token_flag+par_loc;
---
> primitive("par",par_end,256); {cf.\ |scan_file_name|}
5970a7036
> par_loc:=cur_val; par_token:=cs_token_flag+par_loc;
5978c7044
< by |cs_ptr|, which is zero at the end of a file.
---
> by |cur_cs|, which is zero at the end of a file.
5982,5991c7048,7079
< begin if scanner_status≠normal then
< if scanner_status>skipping then @<Tell the user what has run away@>
< else if cs_ptr=0 then
< begin print_nl("! File ended in skipped conditional text");
< @.File ended in skipped...text@>
< help3("This kind of error happens when you say something like")@/
< ("`\if...{...' or `\else{...' and forget the matching `}'.")@/
< ("You probably should type `I}' now."); error;
< end;
< end;
---
> @!q:pointer; {auxiliary pointer}
> begin if scanner_status<>normal then
> begin deletions_allowed:=false;
> @<Back up an outer control sequence so that it can be reread@>;
> if scanner_status>skipping then
> @<Tell the user what has run away and try to recover@>
> else begin print_err("Incomplete "); print_cmd_chr(if_test,cur_if);
> @.Incomplete \\if...@>
> print("; all text was ignored after line "); print_int(skip_line);
> help3("A forbidden control sequence occurred in skipped text.")@/
> ("This kind of error happens when you say `\if...' and forget")@/
> ("the matching `\fi'. I've inserted a `\fi'; this might work.");
> if cur_cs<>0 then cur_cs:=0
> else help_line[2]:=@|
> "The file ended while I was skipping conditional text.";
> cur_tok:=cs_token_flag+frozen_fi; ins_error;
> end;
> deletions_allowed:=true;
> end;
> end;
>
> @ An outer control sequence that occurs in a \.{\\read} will not be reread,
> since the error recovery for \.{\\read} is not very powerful.
>
> @<Back up an outer control sequence so that it can be reread@>=
> if cur_cs<>0 then
> begin if (state=token_list)or(name<1)or(name>17) then
> begin p:=get_avail; info(p):=cs_token_flag+cur_cs;
> back_list(p); {prepare to read the control sequence again}
> end;
> cur_cmd:=spacer; cur_chr:=" "; {replace it by a space}
> end
5993c7081
< @ @<Tell the user what has run away@>=
---
> @ @<Tell the user what has run away...@>=
5995c7083
< if cs_ptr=0 then print_nl("! File ended")
---
> if cur_cs=0 then print_err("File ended")
5997,6000c7085
< else begin p←get_avail; info(p)←cs_token_flag+cs_ptr;
< ins_list(p); {prepare to read the control sequence again}
< cs_ptr←0; cur_cmd←spacer; cur_chr←" "; {replace it by a space}
< print_nl("! Forbidden control sequence found");
---
> else begin cur_cs:=0; print_err("Forbidden control sequence found");
6002c7087
< end;
---
> end;
6004,6008c7089,7090
< case scanner_status of
< defining:print("definition");
< matching:print("use");
< aligning:print("preamble");
< end; {there are no other cases}
---
> @<Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or `\.{text}',
> and insert tokens that should lead to recovery@>;
6012,6014c7094,7096
< ("Either type `I}' to try recovering,")@/
< ("or type 'X' and fix your file.");@/
< deletions_allowed←false; error; deletions_allowed←true;
---
> ("I'll try to recover; but if the error is serious,")@/
> ("you'd better type `E' or `X' now and fix your file.");@/
> error;
6017c7099,7122
< @ We need to mention two procedures here that may be called by |get_next|.
---
> @ The recovery procedure can't be fully understood without knowing more
> about the \TeX\ routines that should be aborted, but we can sketch the
> ideas here: For a runaway definition we will insert a right brace; for a
> runaway preamble, we will insert a special \.{\\cr} token and a right
> brace; and for a runaway argument, we will set |long_state| to
> |outer_call| and insert \.{\\par}.
>
> @<Print either `\.{definition}' or ...@>=
> p:=get_avail;
> case scanner_status of
> defining:begin print("definition"); info(p):=right_brace_token+"}";
> end;
> matching:begin print("use"); info(p):=par_token; long_state:=outer_call;
> end;
> aligning:begin print("preamble"); info(p):=right_brace_token+"}"; q:=p;
> p:=get_avail; link(p):=q; info(p):=cs_token_flag+frozen_cr;
> align_state:=-1000000;
> end;
> absorbing:begin print("text"); info(p):=right_brace_token+"}";
> end;
> end; {there are no other cases}
> ins_list(p)
>
> @ We need to mention a procedure here that may be called by |get_next|.
6019,6020c7124
< @p procedure@?open_err_file; forward;@t\2@>
< procedure@?firm_up_the_line; forward;
---
> @p procedure@?firm_up_the_line; forward;
6022,6023c7126,7128
< @ Now we're ready to take the plunge into |get_next| itself.
< @^mastication@>
---
> @ Now we're ready to take the plunge into |get_next| itself. Parts of
> this routine are executed more often than any other instructions of \TeX.
> @^mastication@>@^inner loop@>
6028c7133
< @p procedure get_next; {sets |cur_cmd|, |cur_chr|, |cs_ptr| to next token}
---
> @p procedure get_next; {sets |cur_cmd|, |cur_chr|, |cur_cs| to next token}
6030,6033c7135,7139
< switch, {go here to eat the next character from a file}
< reswitch, {go here to digest it again}
< start_cs, {go here to start looking for a control sequence}
< found; {go here when a control sequence has been found}
---
> switch, {go here to eat the next character from a file}
> reswitch, {go here to digest it again}
> start_cs, {go here to start looking for a control sequence}
> found, {go here when a control sequence has been found}
> exit; {go here when the next input token has been got}
6036,6037c7142,7146
< begin restart: cs_ptr←0;
< if state≠token_list then
---
> @!cat:0..max_char_code; {|cat_code(cur_chr)|, usually}
> @!c,@!cc:ASCII_code; {constituents of a possible expanded code}
> @!d:2..3; {number of excess characters in an expanded code}
> begin restart: cur_cs:=0;
> if state<>token_list then
6040,6043c7149,7151
< if a parameter needs to be expanded@>;
< if @<End of alignment entry sensed@> then
< @<Insert the \(v)\<v↓j> template and |goto restart|@>;
< end;
---
> if a parameter needs to be expanded@>;
> @<If an alignment entry has just ended, take appropriate action@>;
> exit:end;
6048c7156,7157
< returned to the value it had after the \<u↓j> template for that entry.
---
> returned to the value it had after the \<u_j> template for that entry.
> @^inner loop@>
6050,6051c7159,7161
< @<End of alignment entry sensed@>=
< (align_state=0)∧(cur_cmd≤car_ret)∧(cur_cmd≥tab_mark)
---
> @<If an alignment entry has just ended, take appropriate action@>=
> if cur_cmd<=car_ret then if cur_cmd>=tab_mark then if align_state=0 then
> @<Insert the \(v)\<v_j> template and |goto restart|@>
6054,6066c7164,7179
< begin switch: if loc≤limit then {current line not yet finished}
< begin cur_chr←buffer[loc]; incr(loc);
< reswitch: cur_cmd←ch_code(cur_chr);
< @<Change state if necessary, and |goto switch| if the
< current character should be ignored,
< or |goto reswitch| if the current character
< changes to another@>;
< end
< else begin @<Move to next line of file,
< or |goto restart| if there is no next line@>;
< check_interrupt;
< goto switch;
< end;
---
> @^inner loop@>
> begin switch: if loc<=limit then {current line not yet finished}
> begin cur_chr:=buffer[loc]; incr(loc);
> reswitch: cur_cmd:=cat_code(cur_chr);
> @<Change state if necessary, and |goto switch| if the
> current character should be ignored,
> or |goto reswitch| if the current character
> changes to another@>;
> end
> else begin state:=new_line;@/
> @<Move to next line of file,
> or |goto restart| if there is no next line,
> or |return| if a \.{\\read} line has finished@>;
> check_interrupt;
> goto switch;
> end;
6072c7185
< |(max_char_code+1)| apart from each other, so we can add a character's
---
> apart from each other by |max_char_code+1|, so we can add a character's
6081c7194
< and set |state←skip_blanks|@>;
---
> and set |state:=skip_blanks| or |mid_line|@>;
6083,6085c7196,7199
< and set |state←mid_line|@>;
< any_state_plus(sup_mark): @<If this |sup_mark| starts a control character
< like@@\.{\^\^A}, then |goto reswitch|, otherwise set |state←mid_line|@>;
---
> and set |state:=mid_line|@>;
> any_state_plus(sup_mark): @<If this |sup_mark| starts an expanded character
> like~\.{\^\^A} or~\.{\^\^df}, then |goto reswitch|,
> otherwise set |state:=mid_line|@>;
6087c7201
< |goto switch|@>;
---
> |goto restart|@>;
6095,6096c7209,7214
< @ @<Decry the invalid...@>=
< begin print_nl("! Text line contains an invalid character");
---
> @ We go to |restart| instead of to |switch|, because |state| might equal
> |token_list| after the error has been dealt with
> (cf.\ |clear_for_error_prompt|).
>
> @<Decry the invalid...@>=
> begin print_err("Text line contains an invalid character");
6099,6100c7217,7219
< ("Continue, and I'll forget that it ever happened.");
< goto switch;
---
> ("Continue, and I'll forget that it ever happened.");@/
> deletions_allowed:=false; error; deletions_allowed:=true;
> goto restart;
6103,6104c7222,7223
< @ @d add_delims_to(#)==#+math_delim,#+tab_mark,#+mac_param,
< #+sub_mark,#+letter,#+other_char
---
> @ @d add_delims_to(#)==#+math_shift,#+tab_mark,#+mac_param,
> #+sub_mark,#+letter,#+other_char
6108,6110c7227,7229
< mid_line+car_ret:@<Enter |new_line| state, finish line, emit a space@>;
< skip_blanks+car_ret,any_state_plus(end_line):
< @<Enter |new_line| state, finish line, |goto switch|@>;
---
> mid_line+car_ret:@<Finish line, emit a space@>;
> skip_blanks+car_ret,any_state_plus(comment):
> @<Finish line, |goto switch|@>;
6114,6115c7233,7234
< state←mid_line; incr(align_state);
< end;
---
> state:=mid_line; incr(align_state);
> end;
6118,6120c7237,7239
< state←mid_line; decr(align_state);
< end;
< add_delims_to(skip_blanks),add_delims_to(new_line): state←mid_line;
---
> state:=mid_line; decr(align_state);
> end;
> add_delims_to(skip_blanks),add_delims_to(new_line): state:=mid_line;
6123c7242
< changed to $\.{"\ "}=@'40$. This means that the ascii codes for tab and space,
---
> changed to $\.{"\ "}=@'40$. This means that the ASCII codes for tab and space,
6128,6129c7247,7248
< @<Enter |new_line| state, finish line, emit a space@>=
< begin state←new_line; loc←limit+1; cur_cmd←spacer; cur_chr←" ";
---
> @<Finish line, emit a space@>=
> begin loc:=limit+1; cur_cmd:=spacer; cur_chr:=" ";
6135c7254
< begin state←skip_blanks; cur_chr←" ";
---
> begin state:=skip_blanks; cur_chr:=" ";
6138,6139c7257,7258
< @ @<Enter |new_line| state, finish line, |goto switch|@>=
< begin state←new_line; loc←limit+1; goto switch;
---
> @ @<Finish line, |goto switch|@>=
> begin loc:=limit+1; goto switch;
6143,6145c7262,7264
< begin loc←limit+1; cs_ptr←par_loc; cur_cmd←eq_type(cs_ptr);
< cur_chr←equiv(cs_ptr);
< if cur_cmd≥outer_call then check_outer_validity;
---
> begin loc:=limit+1; cur_cs:=par_loc; cur_cmd:=eq_type(cur_cs);
> cur_chr:=equiv(cur_cs);
> if cur_cmd>=outer_call then check_outer_validity;
6148,6155c7267,7288
< @ @<If this |sup_mark| starts a control character...@>=
< begin if (cur_chr=buffer[loc])∧(loc<limit) then
< if (buffer[loc+1]≤"_")∧(buffer[loc+1]≥"?") then
< begin if buffer[loc+1]="?" then cur_chr←@'177
< else cur_chr←buffer[loc+1]-"@@";
< loc←loc+2; goto reswitch;
< end;
< state←mid_line;
---
> @ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
>
> @d is_hex(#)==(((#>="0")and(#<="9"))or((#>="a")and(#<="f")))
> @d hex_to_cur_chr==
> if c<="9" then cur_chr:=c-"0" @+else cur_chr:=c-"a"+10;
> if cc<="9" then cur_chr:=16*cur_chr+cc-"0"
> else cur_chr:=16*cur_chr+cc-"a"+10
>
> @<If this |sup_mark| starts an expanded character...@>=
> begin if cur_chr=buffer[loc] then if loc<limit then
> begin c:=buffer[loc+1]; @+if c<@'200 then {yes we have an expanded char}
> begin loc:=loc+2;
> if is_hex(c) then if loc<=limit then
> begin cc:=buffer[loc]; @+if is_hex(cc) then
> begin incr(loc); hex_to_cur_chr; goto reswitch;
> end;
> end;
> if c<@'100 then cur_chr:=c+@'100 @+else cur_chr:=c-@'100;
> goto reswitch;
> end;
> end;
> state:=mid_line;
6157c7290
<
---
>
6159,6161c7292,7294
< begin cs_ptr←cur_chr+active_base;
< cur_cmd←eq_type(cs_ptr); cur_chr←equiv(cs_ptr); state←mid_line;
< if cur_cmd≥outer_call then check_outer_validity;
---
> begin cur_cs:=cur_chr+active_base;
> cur_cmd:=eq_type(cur_cs); cur_chr:=equiv(cur_cs); state:=mid_line;
> if cur_cmd>=outer_call then check_outer_validity;
6171c7304
< user tried something like `\.{\\chcode\'15=0}'. The algorithm might
---
> user tried something like `\.{\\catcode\'15=0}'. The algorithm might
6174c7307,7308
< If expanded control characters like `\.{\^\^A}' appear in or just following
---
> If expanded characters like `\.{\^\^A}' or `\.{\^\^df}'
> appear in or just following
6179,6192c7313,7329
< begin if loc>limit then cs_ptr←single_base+null_code
< else begin start_cs: k←loc; cur_chr←buffer[k]; incr(k);
< if (ch_code(cur_chr)=letter)∧(k≤limit) then
< @<Scan ahead in the buffer until finding a nonletter;
< if an expanded control code is encountered, reduce it
< and |goto start_cs|; otherwise if a multiletter control
< sequence is found, adjust |cs_ptr| and |loc|, and
< |goto found|@>
< else @<If an expanded control code is present, reduce it
< and |goto start_cs|@>;
< cs_ptr←single_base+buffer[loc]; incr(loc);
< end;
< found: cur_cmd←eq_type(cs_ptr); cur_chr←equiv(cs_ptr); state←skip_blanks;
< if cur_cmd≥outer_call then check_outer_validity;
---
> begin if loc>limit then cur_cs:=null_cs {|state| is irrelevant in this case}
> else begin start_cs: k:=loc; cur_chr:=buffer[k]; cat:=cat_code(cur_chr);
> incr(k);
> if cat=letter then state:=skip_blanks
> else if cat=spacer then state:=skip_blanks
> else state:=mid_line;
> if (cat=letter)and(k<=limit) then
> @<Scan ahead in the buffer until finding a nonletter;
> if an expanded code is encountered, reduce it
> and |goto start_cs|; otherwise if a multiletter control
> sequence is found, adjust |cur_cs| and |loc|, and
> |goto found|@>
> else @<If an expanded code is present, reduce it and |goto start_cs|@>;
> cur_cs:=single_base+buffer[loc]; incr(loc);
> end;
> found: cur_cmd:=eq_type(cur_cs); cur_chr:=equiv(cur_cs);
> if cur_cmd>=outer_call then check_outer_validity;
6196,6198c7333,7337
< |cur_chr=buffer[k-1]| and |k≤limit+1|. If an expanded control code like
< \.{\^\^A} appears in |buffer[(k-1)..(k+1)]|, we will store the corresponding
< code in |buffer[k-1]| and shift the rest of the buffer left two places.
---
> |cur_chr=buffer[k-1]| and |k<=limit+1| and |cat=cat_code(cur_chr)|. If an
> expanded code like \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)..(k+1)]|
> or |buffer[(k-1)..(k+2)]|, we
> will store the corresponding code in |buffer[k-1]| and shift the rest of
> the buffer left two or three places.
6201,6213c7340,7357
< begin if (buffer[k]=cur_chr)∧(ch_code(cur_chr)=sup_mark)∧@|
< (k<limit) then
< begin cur_chr←buffer[k+1];
< if (cur_chr≤"_")∧(cur_chr≥"?") then
< begin if cur_chr>"?" then buffer[k-1]←cur_chr-"@@"
< else buffer[k-1]←@'177;
< limit←limit-2; first←first-2;
< while k≤limit do
< begin buffer[k]←buffer[k+2]; incr(k);
< end;
< goto start_cs;
< end;
< end;
---
> begin if buffer[k]=cur_chr then @+if cat=sup_mark then @+if k<limit then
> begin c:=buffer[k+1]; @+if c<@'200 then {yes, one is indeed present}
> begin d:=2;
> if is_hex(c) then @+if k+2<=limit then
> begin cc:=buffer[k+2]; @+if is_hex(cc) then incr(d);
> end;
> if d>2 then
> begin hex_to_cur_chr; buffer[k-1]:=cur_chr;
> end
> else if c<@'100 then buffer[k-1]:=c+@'100
> else buffer[k-1]:=c-@'100;
> limit:=limit-d; first:=first-d;
> while k<=limit do
> begin buffer[k]:=buffer[k+d]; incr(k);
> end;
> goto start_cs;
> end;
> end;
6217,6218c7361,7362
< begin repeat cur_chr←buffer[k]; incr(k);
< until (ch_code(cur_chr)≠letter)∨(k>limit);
---
> begin repeat cur_chr:=buffer[k]; cat:=cat_code(cur_chr); incr(k);
> until (cat<>letter)or(k>limit);
6220,6224c7364,7368
< if ch_code(buffer[k-1])≠letter then decr(k);
< {now |k| points to first nonletter}
< if k>loc+1 then {multi-letter control sequence has been scanned}
< begin cs_ptr←id_lookup(loc,k-loc); loc←k; goto found;
< end;
---
> if cat<>letter then decr(k);
> {now |k| points to first nonletter}
> if k>loc+1 then {multiletter control sequence has been scanned}
> begin cur_cs:=id_lookup(loc,k-loc); loc:=k; goto found;
> end;
6230,6249c7374,7411
< if a parameter needs to be expanded@>=
< if loc≠null then {list not exhausted}
< begin t←info(loc); loc←link(loc); {move to next}
< if t≥cs_token_flag then {a control sequence token}
< begin cs_ptr←t-cs_token_flag;
< cur_cmd←eq_type(cs_ptr); cur_chr←equiv(cs_ptr);
< if cur_cmd≥outer_call then check_outer_validity;
< end
< else begin cur_cmd←t div @'400; cur_chr←t mod @'400;
< case cur_cmd of
< left_brace: incr(align_state);
< right_brace: decr(align_state);
< out_param: @<Insert macro parameter and |goto restart|@>;
< othercases do_nothing
< endcases;
< end;
< end
< else begin {we are done with this token list}
< end_token_list; goto restart; {resume previous level}
< end
---
> if a parameter needs to be expanded@>=
> if loc<>null then {list not exhausted}
> @^inner loop@>
> begin t:=info(loc); loc:=link(loc); {move to next}
> if t>=cs_token_flag then {a control sequence token}
> begin cur_cs:=t-cs_token_flag;
> cur_cmd:=eq_type(cur_cs); cur_chr:=equiv(cur_cs);
> if cur_cmd>=outer_call then
> if cur_cmd=dont_expand then
> @<Get the next token, suppressing expansion@>
> else check_outer_validity;
> end
> else begin cur_cmd:=t div @'400; cur_chr:=t mod @'400;
> case cur_cmd of
> left_brace: incr(align_state);
> right_brace: decr(align_state);
> out_param: @<Insert macro parameter and |goto restart|@>;
> othercases do_nothing
> endcases;
> end;
> end
> else begin {we are done with this token list}
> end_token_list; goto restart; {resume previous level}
> end
>
> @ The present point in the program is reached only when the |expand|
> routine has inserted a special marker into the input. In this special
> case, |info(loc)| is known to be a control sequence token, and |link(loc)=null|.
>
> @d no_expand_flag=257 {this characterizes a special variant of |relax|}
>
> @<Get the next token, suppressing expansion@>=
> begin cur_cs:=info(loc)-cs_token_flag; loc:=null;@/
> cur_cmd:=eq_type(cur_cs); cur_chr:=equiv(cur_cs);
> if cur_cmd>max_command then
> begin cur_cmd:=relax; cur_chr:=no_expand_flag;
> end;
> end
6259,6274c7421,7447
< @<Move to next line of file, or |goto restart| if there is no next line@>=
< if not terminal_input then @<Read next line of file into |buffer|, or
< |goto restart| if the file has ended@>
< else begin if input_ptr>0 then {text was inserted during error recovery}
< begin end_file_reading; goto restart; {resume previous level}
< end;
< if selector<err_only then open_err_file;
< if interaction>nonstop_mode then
< begin if limit=start then {previous line was empty}
< print_nl("(Type a command or say `\end')");
< print_ln; first←start;
< prompt_input("*"); {input on-line into |buffer|}
< limit←last; buffer[limit]←carriage_return; first←last+1;
< loc←start;
< end
< else fatal_error("*** (job aborted, no legal \end found)");
---
> @d end_line_char_inactive == (end_line_char<0)or(end_line_char>255)
>
> @<Move to next line of file, or |goto restart|...@>=
> if name>17 then @<Read next line of file into |buffer|, or
> |goto restart| if the file has ended@>
> else begin if not terminal_input then {\.{\\read} line has ended}
> begin cur_cmd:=0; cur_chr:=0; return;
> end;
> if input_ptr>0 then {text was inserted during error recovery}
> begin end_file_reading; goto restart; {resume previous level}
> end;
> if selector<log_only then open_log_file;
> if interaction>nonstop_mode then
> begin if end_line_char_inactive then incr(limit);
> if limit=start then {previous line was empty}
> print_nl("(Please type a command or say `\end')");
> @.Please type...@>
> print_ln; first:=start;
> prompt_input("*"); {input on-line into |buffer|}
> @.*\relax@>
> limit:=last;
> if end_line_char_inactive then decr(limit)
> else buffer[limit]:=end_line_char;
> first:=limit+1;
> loc:=start;
> end
> else fatal_error("*** (job aborted, no legal \end found)");
6276,6278c7449,7457
< {nonstop mode, which is intended for overnight batch processing,
< never waits for on-line input}
< end
---
> {nonstop mode, which is intended for overnight batch processing,
> never waits for on-line input}
> end
>
> @ The global variable |force_eof| is normally |false|; it is set |true|
> by an \.{\\endinput} command.
>
> @<Glob...@>=
> @!force_eof:boolean; {should the next \.{\\input} be aborted early?}
6281,6290c7460,7476
< |goto restart| if the file has ended@>=
< begin incr(line); first←start;
< if input_ln(cur_file) then {not end of file}
< firm_up_the_line {this sets |limit|}
< else if limit≠start then limit←start {insert empty line at end of file}
< else begin check_outer_validity;
< print_char(")"); update_terminal; {show user that file has been read}
< end_file_reading; goto restart; {resume previous level}
< end;
< buffer[limit]←carriage_return; first←limit+1; loc←start; {ready to read}
---
> |goto restart| if the file has ended@>=
> begin incr(line); first:=start;
> if not force_eof then
> begin if input_ln(cur_file,true) then {not end of file}
> firm_up_the_line {this sets |limit|}
> else force_eof:=true;
> end;
> if force_eof then
> begin print_char(")"); decr(open_parens);
> update_terminal; {show user that file has been read}
> force_eof:=false;
> end_file_reading; {resume previous level}
> check_outer_validity; goto restart;
> end;
> if end_line_char_inactive then decr(limit)
> else buffer[limit]:=end_line_char;
> first:=limit+1; loc:=start; {ready to read}
6293c7479
< @ If the user has set the |pause| parameter to some nonzero value,
---
> @ If the user has set the |pausing| parameter to some positive value,
6302,6312c7488,7499
< begin limit←last;
< if (pause≠0)∧(interaction>nonstop_mode) then
< begin print_ln;
< if start<limit then for k←start to limit-1 do print(buffer[k]);
< first←limit; prompt_input("=>"); {wait for user response}
< if last>first then
< begin for k←first to last-1 do {move line down in buffer}
< buffer[k+start-first]←buffer[k];
< limit←start+last-first;
< end;
< end;
---
> begin limit:=last;
> if pausing>0 then if interaction>nonstop_mode then
> begin wake_up_terminal; print_ln;
> if start<limit then for k:=start to limit-1 do print(buffer[k]);
> first:=limit; prompt_input("=>"); {wait for user response}
> @.=>@>
> if last>first then
> begin for k:=first to last-1 do {move line down in buffer}
> buffer[k+start-first]:=buffer[k];
> limit:=start+last-first;
> end;
> end;
6321c7508
< \yskip\hang|get_nc_token|, meaning ``get the non-|call| token,'' is like
---
> \yskip\hang|get_x_token|, meaning ``get an expanded token,'' is like
6323,6324c7510,7513
< control sequence (i.e., a macro call), or something like \.{\\topmark},
< it is eliminated from the input by beginning the expansion of the macro.
---
> control sequence (i.e., a macro call), or a conditional,
> or something like \.{\\topmark} or \.{\\expandafter} or \.{\\csname},
> it is eliminated from the input by beginning the expansion of the macro
> or the evaluation of the conditional.
6326c7515
< \yskip\hang|nc_token| is like |get_nc_token| except that it assumes that
---
> \yskip\hang|x_token| is like |get_x_token| except that it assumes that
6330,6331c7519
< In fact, these three procedures account for {\sl all\/} uses of |get_next|,
< except for one place in the ``inner loop'' when |cur_tok| need not be set.
---
> In fact, these three procedures account for almost every use of |get_next|.
6333,6344c7521,7523
< @ The |get_token| routine also has a special test built into it to make
< sure that the token found is not `|endv|', i.e., the end of a template,
< since this would be a bad case of misalignment. We wouldn't want an |endv|
< to be deleted or to infiltrate another token list, and |get_token| is used
< only in the |error| routine or when building token lists, or in a few
< other very special cases. The |endv| problem
< hardly ever arises in a form where recovery is feasible, so no recovery
< mechanism has been provided.
<
< No new control sequences will be defined except during a call of
< |get_token|, because |no_new_control_sequence| is always |true| at
< other times.
---
> @ No new control sequences will be defined except during a call of
> |get_token|, or when \.{\\csname} compresses a token list, because
> |no_new_control_sequence| is always |true| at other times.
6347,6373c7526,7574
< begin no_new_control_sequence←false; get_next; no_new_control_sequence←true;
< if cs_ptr=0 then
< begin if cur_cmd=endv then
< fatal_error("(alignment is fouled up somehow)");
< @.alignment is fouled up@>
< cur_tok←(cur_cmd*@'400)+cur_chr;
< end
< else cur_tok←cs_token_flag+cs_ptr;
< end;
<
< @ The only command codes |>max_command| that can be returned by the
< |get_next| routine are |undefined_cs|, |top_bot_mark|, |call|, |long_call|,
< |outer_call|, and |long_outer_call|, in increasing numerical order.
<
< @p@t\4@>@<Declare the procedure called |macro_call|@>
< procedure get_nc_token; {sets |cur_cmd|, |cur_chr|, |cur_tok|,
< and expands macros}
< label done;
< begin loop begin get_next;
< if cur_cmd≤max_command then goto done;
< if cur_cmd≥call then macro_call
< else if cur_cmd=top_bot_mark then
< @<Insert the \(a)appropriate mark text into the scanner@>
< else @<Complain about an undefined macro@>;
< end;
< done: if cs_ptr=0 then cur_tok←(cur_cmd*@'400)+cur_chr
< else cur_tok←cs_token_flag+cs_ptr;
---
> begin no_new_control_sequence:=false; get_next; no_new_control_sequence:=true;
> @^inner loop@>
> if cur_cs=0 then cur_tok:=(cur_cmd*@'400)+cur_chr
> else cur_tok:=cs_token_flag+cur_cs;
> end;
>
> @* \[25] Expanding the next token.
> Only a dozen or so command codes |>max_command| can possibly be returned by
> |get_next|; in increasing order, they are |undefined_cs|, |expand_after|,
> |no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|,
> |top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and
> |end_template|.{\emergencystretch=40pt\par}
>
> The |expand| subroutine is used when |cur_cmd>max_command|. It removes a
> ``call'' or a conditional or one of the other special operations just
> listed. It follows that |expand| might invoke itself recursively. In all
> cases, |expand| destroys the current token, but it sets things up so that
> the next |get_next| will deliver the appropriate next token. The value of
> |cur_tok| need not be known when |expand| is called.
>
> Since several of the basic scanning routines communicate via global variables,
> their values are saved as local variables of |expand| so that
> recursive calls don't invalidate them.
> @^recursion@>
>
> @p@t\4@>@<Declare the procedure called |macro_call|@>@;@/
> @t\4@>@<Declare the procedure called |insert_relax|@>@;@/
> procedure@?pass_text; forward;@t\2@>
> procedure@?start_input; forward;@t\2@>
> procedure@?conditional; forward;@t\2@>
> procedure@?get_x_token; forward;@t\2@>
> procedure@?conv_toks; forward;@t\2@>
> procedure@?ins_the_toks; forward;@t\2@>
> procedure expand;
> var t:halfword; {token that is being ``expanded after''}
> @!p,@!q,@!r:pointer; {for list manipulation}
> @!j:0..buf_size; {index into |buffer|}
> @!cv_backup:integer; {to save the global quantity |cur_val|}
> @!cvl_backup,@!radix_backup,@!co_backup:small_number;
> {to save |cur_val_level|, etc.}
> @!backup_backup:pointer; {to save |link(backup_head)|}
> @!save_scanner_status:small_number; {temporary storage of |scanner_status|}
> begin cv_backup:=cur_val; cvl_backup:=cur_val_level; radix_backup:=radix;
> co_backup:=cur_order; backup_backup:=link(backup_head);
> if cur_cmd<call then @<Expand a nonmacro@>
> else if cur_cmd<end_template then macro_call
> else @<Insert a token containing |frozen_endv|@>;
> cur_val:=cv_backup; cur_val_level:=cvl_backup; radix:=radix_backup;
> cur_order:=co_backup; link(backup_head):=backup_backup;
6375a7577,7618
> @ @<Expand a nonmacro@>=
> begin if tracing_commands>1 then show_cur_cmd_chr;
> case cur_cmd of
> top_bot_mark:@<Insert the \(a)appropriate mark text into the scanner@>;
> expand_after:@<Expand the token after the next token@>;
> no_expand:@<Suppress expansion of the next token@>;
> cs_name:@<Manufacture a control sequence name@>;
> convert:conv_toks; {this procedure is discussed in Part 27 below}
> the:ins_the_toks; {this procedure is discussed in Part 27 below}
> if_test:conditional; {this procedure is discussed in Part 28 below}
> fi_or_else:@<Terminate the current conditional and skip to \.{\\fi}@>;
> input:@<Initiate or terminate input from a file@>;
> othercases @<Complain about an undefined macro@>
> endcases;
> end
>
> @ It takes only a little shuffling to do what \TeX\ calls \.{\\expandafter}.
>
> @<Expand the token after...@>=
> begin get_token; t:=cur_tok; get_token;
> if cur_cmd>max_command then expand@+else back_input;
> cur_tok:=t; back_input;
> end
>
> @ The implementation of \.{\\noexpand} is a bit trickier, because it is
> necessary to insert a special `|dont_expand|' marker into \TeX's reading
> mechanism. This special marker is processed by |get_next|, but it does
> not slow down the inner loop.
>
> Since \.{\\outer} macros might arise here, we must also
> clear the |scanner_status| temporarily.
>
> @<Suppress expansion...@>=
> begin save_scanner_status:=scanner_status; scanner_status:=normal;
> get_token; scanner_status:=save_scanner_status; t:=cur_tok;
> back_input; {now |start| and |loc| point to the backed-up token |t|}
> if t>=cs_token_flag then
> begin p:=get_avail; info(p):=cs_token_flag+frozen_dont_expand;
> link(p):=loc; start:=p; loc:=p;
> end;
> end
>
6377c7620
< begin print_nl("! Undefined control sequence");
---
> begin print_err("Undefined control sequence");
6387,6388c7630,7632
< @ The |get_nc_token| procedure is equivalent to two consecutive
< procedure calls: |get_next; nc_token|.
---
> @ The |expand| procedure and some other routines that construct token
> lists find it convenient to use the following macros, which are valid only if
> the variables |p| and |q| are reserved for token-list building.
6390,6400c7634,7747
< @p procedure nc_token; {|get_nc_token| without the initial |get_next|}
< label done;
< begin loop begin if cur_cmd≤max_command then goto done;
< if cur_cmd≥call then macro_call
< else if cur_cmd=top_bot_mark then
< @<Insert the \(a)appropriate mark text into the scanner@>
< else @<Complain about an undefined macro@>;
< get_next;
< end;
< done: if cs_ptr=0 then cur_tok←(cur_cmd*@'400)+cur_chr
< else cur_tok←cs_token_flag+cs_ptr;
---
> @d store_new_token(#)==begin q:=get_avail; link(p):=q; info(q):=#;
> p:=q; {|link(p)| is |null|}
> end
> @d fast_store_new_token(#)==begin fast_get_avail(q); link(p):=q; info(q):=#;
> p:=q; {|link(p)| is |null|}
> end
>
> @ @<Manufacture a control...@>=
> begin r:=get_avail; p:=r; {head of the list of characters}
> repeat get_x_token;
> if cur_cs=0 then store_new_token(cur_tok);
> until cur_cs<>0;
> if cur_cmd<>end_cs_name then @<Complain about missing \.{\\endcsname}@>;
> @<Look up the characters of list |r| in the hash table, and set |cur_cs|@>;
> flush_list(r);
> if eq_type(cur_cs)=undefined_cs then
> begin eq_define(cur_cs,relax,256); {N.B.: The |save_stack| might change}
> end; {the control sequence will now match `\.{\\relax}'}
> cur_tok:=cur_cs+cs_token_flag; back_input;
> end
>
> @ @<Complain about missing \.{\\endcsname}@>=
> begin print_err("Missing "); print_esc("endcsname"); print(" inserted");
> @.Missing \\endcsname...@>
> help2("The control sequence marked <to be read again> should")@/
> ("not appear between \csname and \endcsname.");
> back_error;
> end
>
> @ @<Look up the characters of list |r| in the hash table...@>=
> j:=first; p:=link(r);
> while p<>null do
> begin if j>=max_buf_stack then
> begin max_buf_stack:=j+1;
> if max_buf_stack=buf_size then
> overflow("buffer size",buf_size);
> @:TeX capacity exceeded buffer size}{\quad buffer size@>
> end;
> buffer[j]:=info(p) mod @'400; incr(j); p:=link(p);
> end;
> if j>first+1 then
> begin no_new_control_sequence:=false; cur_cs:=id_lookup(first,j-first);
> no_new_control_sequence:=true;
> end
> else if j=first then cur_cs:=null_cs {the list is empty}
> else cur_cs:=single_base+buffer[first] {the list has length one}
>
> @ An |end_template| command is effectively changed to an |endv| command
> by the following code. (The reason for this is discussed below; the
> |frozen_end_template| at the end of the template has passed the
> |check_outer_validity| test, so its mission of error detection has been
> accomplished.)
>
> @<Insert a token containing |frozen_endv|@>=
> begin cur_tok:=cs_token_flag+frozen_endv; back_input;
> end
>
> @ The processing of \.{\\input} involves the |start_input| subroutine,
> which will be declared later; the processing of \.{\\endinput} is trivial.
>
> @<Put each...@>=
> primitive("input",input,0);@/
> @!@:input_}{\.{\\input} primitive@>
> primitive("endinput",input,1);@/
> @!@:end_input_}{\.{\\endinput} primitive@>
>
> @ @<Cases of |print_cmd_chr|...@>=
> input: if chr_code=0 then print_esc("input")@+else print_esc("endinput");
>
> @ @<Initiate or terminate input...@>=
> if cur_chr>0 then force_eof:=true
> else if name_in_progress then insert_relax
> else start_input
>
> @ Sometimes the expansion looks too far ahead, so we want to insert
> a harmless \.{\\relax} into the user's input.
>
> @<Declare the procedure called |insert_relax|@>=
> procedure insert_relax;
> begin cur_tok:=cs_token_flag+cur_cs; back_input;
> cur_tok:=cs_token_flag+frozen_relax; back_input; token_type:=inserted;
> end;
>
> @ Here is a recursive procedure that is \TeX's usual way to get the
> next token of input. It has been slightly optimized to take account of
> common cases.
>
> @p procedure get_x_token; {sets |cur_cmd|, |cur_chr|, |cur_tok|,
> and expands macros}
> label restart,done;
> begin restart: get_next;
> @^inner loop@>
> if cur_cmd<=max_command then goto done;
> if cur_cmd>=call then
> if cur_cmd<end_template then macro_call
> else begin cur_cs:=frozen_endv; cur_cmd:=endv;
> goto done; {|cur_chr=null_list|}
> end
> else expand;
> goto restart;
> done: if cur_cs=0 then cur_tok:=(cur_cmd*@'400)+cur_chr
> else cur_tok:=cs_token_flag+cur_cs;
> end;
>
> @ The |get_x_token| procedure is equivalent to two consecutive
> procedure calls: |get_next; x_token|.
>
> @p procedure x_token; {|get_x_token| without the initial |get_next|}
> begin while cur_cmd>max_command do
> begin expand;
> get_next;
> end;
> if cur_cs=0 then cur_tok:=(cur_cmd*@'400)+cur_chr
> else cur_tok:=cs_token_flag+cur_cs;
6402,6403c7749,7750
< @* \[25] Expanding user macros.
< A control sequence that has been \.{\\def}'ed by the user is expanded by
---
>
> @ A control sequence that has been \.{\\def}'ed by the user is expanded by
6426c7773
< {token lists for marks}
---
> {token lists for marks}
6429,6430c7776,7777
< top_mark←null; first_mark←null; bot_mark←null;
< split_first_mark←null; split_bot_mark←null;
---
> top_mark:=null; first_mark:=null; bot_mark:=null;
> split_first_mark:=null; split_bot_mark:=null;
6446,6451c7793,7798
< first_mark_code: print_esc("firstmark");
< bot_mark_code: print_esc("botmark");
< split_first_mark_code: print_esc("splitfirstmark");
< split_bot_mark_code: print_esc("splitbotmark");
< othercases print_esc("topmark")
< endcases;
---
> first_mark_code: print_esc("firstmark");
> bot_mark_code: print_esc("botmark");
> split_first_mark_code: print_esc("splitfirstmark");
> split_bot_mark_code: print_esc("splitbotmark");
> othercases print_esc("topmark")
> endcases;
6457,6458c7804,7805
< begin if cur_mark[cur_chr]≠null then
< begin_token_list(cur_mark[cur_chr],mark_text);
---
> begin if cur_mark[cur_chr]<>null then
> begin_token_list(cur_mark[cur_chr],mark_text);
6461,6464c7808,7821
< @ Now let's consider |macro_call| itself, which is invoked when |cur_cmd|
< is either |call|, |long_call|, |outer_call|, or |long_outer_call|. The
< control sequence definition appears in the token list whose reference count
< is in location |cur_chr| of |mem|.
---
> @ Now let's consider |macro_call| itself, which is invoked when \TeX\ is
> scanning a control sequence whose |cur_cmd| is either |call|, |long_call|,
> |outer_call|, or |long_outer_call|. The control sequence definition
> appears in the token list whose reference count is in location |cur_chr|
> of |mem|.
>
> The global variable |long_state| will be set to |call| or to |long_call|,
> depending on whether or not the control sequence disallows \.{\\par}
> in its parameters. The |get_next| routine will set |long_state| to
> |outer_call| and emit \.{\\par}, if a file ends or if an \.{\\outer}
> control sequence occurs in the midst of an argument.
>
> @<Glob...@>=
> @!long_state:call..long_outer_call; {governs the acceptance of \.{\\par}}
6466,6467c7823,7824
< The parameters, if any, must be scanned before the macro is expanded.
< Pa\-ram\-eters are token lists without reference counts. They are placed on
---
> @ The parameters, if any, must be scanned before the macro is expanded.
> Parameters are token lists without reference counts. They are placed on
6480c7837
< top of the \TeX's input stack, so that |get_next| will proceed to read it
---
> top of\/ \TeX's input stack, so that |get_next| will proceed to read it
6483c7840
< The global variable |cs_ptr| contains the |eqtb| address of the control sequence
---
> The global variable |cur_cs| contains the |eqtb| address of the control sequence
6504d7860
< @!non_long:boolean; {is \.{\\par} forbidden in parameters?}
6507,6512c7863,7869
< begin save_scanner_status←scanner_status; save_warning_index←warning_index;
< warning_index←cs_ptr; ref_count←cur_chr; r←link(ref_count); n←0;
< if tracing_macros≠0 then @<Show the text of the macro being expanded@>;
< if info(r)≠end_match_token then
< @<Scan the parameters and make |link(r)| point to the macro body; but
< |return| if an illegal \.{\\par} is detected@>;
---
> @!match_chr:ASCII_code; {character used in parameter}
> begin save_scanner_status:=scanner_status; save_warning_index:=warning_index;
> warning_index:=cur_cs; ref_count:=cur_chr; r:=link(ref_count); n:=0;
> if tracing_macros>0 then @<Show the text of the macro being expanded@>;
> if info(r)<>end_match_token then
> @<Scan the parameters and make |link(r)| point to the macro body; but
> |return| if an illegal \.{\\par} is detected@>;
6514c7871
< exit:scanner_status←save_scanner_status; warning_index←save_warning_index;
---
> exit:scanner_status:=save_scanner_status; warning_index:=save_warning_index;
6522,6523c7879,7881
< while (state=token_list)∧(loc=null) do end_token_list; {conserve stack space}
< begin_token_list(ref_count,macro); name←warning_index; loc←link(r);
---
> while (state=token_list)and(loc=null)and(token_type<>v_template) do
> end_token_list; {conserve stack space}
> begin_token_list(ref_count,macro); name:=warning_index; loc:=link(r);
6525,6540c7883,7891
< begin if param_ptr+n>max_param_stack then
< begin max_param_stack←param_ptr+n;
< if max_param_stack>param_size then
< overflow("parameter stack size",param_size);
< end;
< for m←0 to n-1 do param_stack[param_ptr+m]←pstack[m];
< param_ptr←param_ptr+n;
< end
<
< @ The |macro_call| procedure and some other routines that construct token lists
< find it convenient to use the following macro, which is valid only if
< the variables |p| and |q| are reserved for token-list building.
<
< @d store_new_token(#)==begin q←get_avail; link(p)←q; info(q)←#;
< p←q; {|link(p)| is |null|}
< end
---
> begin if param_ptr+n>max_param_stack then
> begin max_param_stack:=param_ptr+n;
> if max_param_stack>param_size then
> overflow("parameter stack size",param_size);
> @:TeX capacity exceeded parameter stack size}{\quad parameter stack size@>
> end;
> for m:=0 to n-1 do param_stack[param_ptr+m]:=pstack[m];
> param_ptr:=param_ptr+n;
> end
6553,6558c7904,7911
< begin scanner_status←matching; unbalance←0;
< if (eq_type(cs_ptr)=call)∨(eq_type(cs_ptr)=outer_call) then non_long←true
< else non_long←false;
< repeat if (info(r)>match_token+255)∨(info(r)<match_token) then s←null
< else begin s←link(r); r←s; p←temp_head; link(p)←null; m←0;
< end;
---
> begin scanner_status:=matching; unbalance:=0;
> long_state:=eq_type(cur_cs);
> if long_state>=outer_call then long_state:=long_state-2;
> repeat link(temp_head):=null;
> if (info(r)>match_token+255)or(info(r)<match_token) then s:=null
> else begin match_chr:=info(r)-match_token; s:=link(r); r:=s;
> p:=temp_head; m:=0;
> end;
6560c7913
< simply scan the delimiter string@>;@/
---
> simply scan the delimiter string@>;@/
6566c7919
< any token found by |get_token|. Therefore an undelimiter parameter---i.e.,
---
> any token found by |get_token|. Therefore an undelimited parameter---i.e.,
6573,6574c7926,7927
< @<Advance \(r)|r|; |goto found| if the parameter delimiter has been
< fully matched, otherwise |goto continue|@>;
---
> @<Advance \(r)|r|; |goto found| if the parameter delimiter has been
> fully matched, otherwise |goto continue|@>;
6576,6578c7929,7932
< |goto continue| if a partial match is still in effect;
< but abort if |s=null|@>;
< if (cur_tok=par_token)∧non_long then @<Report a runaway argument and abort@>;
---
> |goto continue| if a partial match is still in effect;
> but abort if |s=null|@>;
> if cur_tok=par_token then if long_state<>long_call then
> @<Report a runaway argument and abort@>;
6580,6583c7934,7938
< if cur_tok<left_brace_limit then
< @<Contribute an entire group to the current parameter@>
< else @<Report an extra right brace and |goto continue|@>
< else store_new_token(cur_tok);
---
> if cur_tok<left_brace_limit then
> @<Contribute an entire group to the current parameter@>
> else @<Report an extra right brace and |goto continue|@>
> else @<Store the current token, but |goto continue| if it is
> a blank space that would become an undelimited parameter@>;
6585,6586c7940,7949
< if (info(r)<match_token)∨(info(r)>end_match_token) then goto continue;
< found: if s≠null then @<Tidy up the parameter just scanned, and tuck it away@>
---
> if info(r)>end_match_token then goto continue;
> if info(r)<match_token then goto continue;
> found: if s<>null then @<Tidy up the parameter just scanned, and tuck it away@>
>
> @ @<Store the current token, but |goto continue| if it is...@>=
> begin if cur_tok=space_token then
> if info(r)<=end_match_token then
> if info(r)>=match_token then goto continue;
> store_new_token(cur_tok);
> end
6594,6598c7957,7961
< begin r←link(r);
< if (info(r)≥match_token)∧(info(r)≤end_match_token) then
< begin if cur_tok<left_brace_limit then decr(align_state);
< goto found;
< end
---
> begin r:=link(r);
> if (info(r)>=match_token)and(info(r)<=end_match_token) then
> begin if cur_tok<left_brace_limit then decr(align_state);
> goto found;
> end
6603c7966,7967
< begin print_nl("Argument of "); sprint_cs(warning_index);
---
> begin back_input; print_err("Argument of "); sprint_cs(warning_index);
> @.Argument of \\x has...@>
6605,6611c7969,7979
< help5("I've deleted a `}' that doesn't seem to match anything.")@/
< ("For example, `\def\a#1{...}' and `\a}' would produce this")@/
< ("error. If your `}' was spurious, just proceed. Otherwise,")@/
< ("type `I\par}' (including the `}') and I'll tell you about")@/
< ("a runaway argument that might be the root of the problem.");
< incr(align_state); non_long←true; error;
< end
---
> help6("I've run across a `}' that doesn't seem to match anything.")@/
> ("For example, `\def\a#1{...}' and `\a}' would produce")@/
> ("this error. If you simply proceed now, the `\par' that")@/
> ("I've just inserted will cause me to report a runaway")@/
> ("argument that might be the root of the problem. But if")@/
> ("your `}' was spurious, just type `2' and it will go away.");
> incr(align_state); long_state:=call; cur_tok:=par_token; ins_error;
> goto continue;
> end {a white lie; the \.{\\par} won't always trigger a runaway}
>
> @ If |long_state=outer_call|, a runaway argument has already been reported.
6613,6614c7981,7983
< @ @<Report a runaway argument and abort@>=
< begin runaway; print_nl("! Paragraph ended before "); sprint_cs(warning_index);
---
> @<Report a runaway argument and abort@>=
> begin if long_state=call then
> begin runaway; print_err("Paragraph ended before ");
6616,6622c7985,7993
< print(" was complete");
< help3("I suspect you've forgotten a `}', causing me to apply this")@/
< ("control sequence to too much text. How can we recover?")@/
< ("My plan is to forget the whole thing and hope for the best.");
< pstack[n]←link(temp_head); align_state←align_state-unbalance;
< for m←0 to n do flush_list(pstack[m]);
< back_error; return;
---
> sprint_cs(warning_index); print(" was complete");
> help3("I suspect you've forgotten a `}', causing me to apply this")@/
> ("control sequence to too much text. How can we recover?")@/
> ("My plan is to forget the whole thing and hope for the best.");
> back_error;
> end;
> pstack[n]:=link(temp_head); align_state:=align_state-unbalance;
> for m:=0 to n do flush_list(pstack[m]);
> return;
6626c7997
< the predecessor of |r|, and we have found that |cur_tok≠info(r)|. An
---
> the predecessor of |r|, and we have found that |cur_tok<>info(r)|. An
6642,6655c8013,8027
< if s≠r then
< if s=null then @<Report an improper use of the macro and abort@>
< else begin t←s;
< repeat store_new_token(info(t)); incr(m); u←link(t); v←s;
< loop@+ begin if u=r then
< if cur_tok≠info(v) then goto done
< else begin r←link(v); goto continue;
< end;
< if info(u)≠info(v) then goto done;
< u←link(u); v←link(v);
< end;
< done: t←link(t);
< until t=r;
< end
---
> if s<>r then
> if s=null then @<Report an improper use of the macro and abort@>
> else begin t:=s;
> repeat store_new_token(info(t)); incr(m); u:=link(t); v:=s;
> loop@+ begin if u=r then
> if cur_tok<>info(v) then goto done
> else begin r:=link(v); goto continue;
> end;
> if info(u)<>info(v) then goto done;
> u:=link(u); v:=link(v);
> end;
> done: t:=link(t);
> until t=r;
> r:=s; {at this point, no tokens are recently matched}
> end
6658c8030
< begin print_nl("! Use of "); sprint_cs(warning_index);
---
> begin print_err("Use of "); sprint_cs(warning_index);
6662,6664c8034,8036
< ("put `1' after `\a', since control sequence names are")@/
< ("made up of letters only. The macro here has not been")@/
< ("followed by the required stuff, so I'm ignoring it.");
---
> ("put `1' after `\a', since control sequence names are")@/
> ("made up of letters only. The macro here has not been")@/
> ("followed by the required stuff, so I'm ignoring it.");
6669,6679c8041,8052
< begin unbalance←1;
< loop@+ begin store_new_token(cur_tok); get_token;
< if (cur_tok=par_token)∧ non_long then
< @<Report a runaway argument and abort@>;
< if cur_tok<right_brace_limit then
< if cur_tok<left_brace_limit then incr(unbalance)
< else begin decr(unbalance);
< if unbalance=0 then goto done1;
< end;
< end;
< done1: rbrace_ptr←p; store_new_token(cur_tok);
---
> begin unbalance:=1;
> @^inner loop@>
> loop@+ begin fast_store_new_token(cur_tok); get_token;
> if cur_tok=par_token then if long_state<>long_call then
> @<Report a runaway argument and abort@>;
> if cur_tok<right_brace_limit then
> if cur_tok<left_brace_limit then incr(unbalance)
> else begin decr(unbalance);
> if unbalance=0 then goto done1;
> end;
> end;
> done1: rbrace_ptr:=p; store_new_token(cur_tok);
6686,6690c8059,8063
< begin if (m=1)∧(info(p)<right_brace_limit)∧(p≠temp_head) then
< begin link(rbrace_ptr)←null; free_avail(p);
< p←link(temp_head); pstack[n]←link(p); free_avail(p);
< end
< else pstack[n]←link(temp_head);
---
> begin if (m=1)and(info(p)<right_brace_limit)and(p<>temp_head) then
> begin link(rbrace_ptr):=null; free_avail(p);
> p:=link(temp_head); pstack[n]:=link(p); free_avail(p);
> end
> else pstack[n]:=link(temp_head);
6692,6696c8065,8069
< if tracing_macros≠0 then
< begin begin_diagnostic; print_nl("#"); print_int(n);
< print("<-"); show_token_list(pstack[n-1],null,1000);
< end_diagnostic;
< end;
---
> if tracing_macros>0 then
> begin begin_diagnostic; print_nl(match_chr); print_int(n);
> print("<-"); show_token_list(pstack[n-1],null,1000);
> end_diagnostic(false);
> end;
6701c8074
< token_show(ref_count); end_diagnostic;
---
> token_show(ref_count); end_diagnostic(false);
6702a8076
>
6706c8080,8081
< some are quite elaborate.
---
> some are quite elaborate. Almost all of the routines call |get_x_token|,
> which can cause them to be invoked recursively.
6707a8083
> @^recursion@>
6711c8087,8088
< a character whose chcode is |left_brace|.)
---
> a character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to
> appear before the |left_brace|.
6714,6729c8091,8106
< begin @<Get the next non-blank non-call token@>;
< if cur_cmd≠left_brace then
< begin print_nl("! Missing { inserted");
< @.Missing {\{} inserted@>
< help4("A left brace was mandatory here, so I've put one in.")@/
< ("You might want to delete and/or insert some corrections")@/
< ("so that I will find a matching right brace soon.")@/
< ("If you're confused by all this, try typing `I}' now.");
< back_error; cur_tok←left_brace_token+"{"; cur_cmd←left_brace;
< cur_chr←"{"; incr(align_state);
< end;
< end;
<
< @ @<Get the next non-blank non-call...@>=
< repeat get_nc_token;
< until cur_cmd≠spacer
---
> begin @<Get the next non-blank non-relax non-call token@>;
> if cur_cmd<>left_brace then
> begin print_err("Missing { inserted");
> @.Missing \{ inserted@>
> help4("A left brace was mandatory here, so I've put one in.")@/
> ("You might want to delete and/or insert some corrections")@/
> ("so that I will find a matching right brace soon.")@/
> ("(If you're confused by all this, try typing `I}' now.)");
> back_error; cur_tok:=left_brace_token+"{"; cur_cmd:=left_brace;
> cur_chr:="{"; incr(align_state);
> end;
> end;
>
> @ @<Get the next non-blank non-relax non-call token@>=
> repeat get_x_token;
> until (cur_cmd<>spacer)and(cur_cmd<>relax)
6732c8109
< by optional spaces.
---
> by optional spaces; `\.{\\relax}' is not ignored here.
6735,6759c8112,8113
< begin @<Get the next non-blank non-call...@>;
< if cur_tok≠other_token+"=" then back_input;
< end;
<
< @ Here is a procedure that ignores text until coming to a right brace at
< level zero, assuming that we start at a given level |l| of nested braces.
< The closing right brace is followed by an optional space.
<
< @p procedure pass_block(@!l:integer);
< label done;
< begin scanner_status←skipping;
< loop@+ begin get_token;
< if cur_cmd=right_brace then
< begin l←l-1;
< if l≤0 then goto done;
< end
< else if cur_cmd=left_brace then l←l+1;
< end;
< done: if l<0 then
< begin incr(align_state); print_nl("! Missing { inserted");
< @.Missing {\{} inserted@>
< help2("There should have been a `{' before the `}' that")@/
< ("I just looked at. So I put one in."); error;
< end;
< scanner_status←normal; @<Scan an optional space@>;
---
> begin @<Get the next non-blank non-call token@>;
> if cur_tok<>other_token+"=" then back_input;
6762,6764c8116,8118
< @ @<Scan an optional space@>=
< begin get_nc_token; if cur_cmd≠spacer then back_input;
< end
---
> @ @<Get the next non-blank non-call token@>=
> repeat get_x_token;
> until cur_cmd<>spacer
6767,6768c8121,8122
< Given a string of lower case letters, like `\.{pt}' or `\.{plus}' or
< `\.{after}', the |scan_keyword| routine checks to see whether the next
---
> Given a string of lowercase letters, like `\.{pt}' or `\.{plus}' or
> `\.{width}', the |scan_keyword| routine checks to see whether the next
6770c8124
< upper case letters will match their lower case counterparts; upper case
---
> uppercase letters will match their lowercase counterparts; uppercase
6779c8133
<
---
> @^inner loop@>
6786c8140
< begin p←scan_head; link(p)←null; k←str_start[s];
---
> begin p:=backup_head; link(p):=null; k:=str_start[s];
6788,6798c8142,8154
< begin get_nc_token;
< if (cur_cmd=letter)∧@|
< ((cur_chr=str_pool[k])∨(cur_chr=str_pool[k]-"a"+"A")) then
< begin store_new_token(cur_tok); incr(k)
< end
< else begin back_input;
< if p≠scan_head then ins_list(link(scan_head));
< scan_keyword←false; return;
< end;
< end;
< flush_list(link(scan_head)); scan_keyword←true;
---
> begin get_x_token; {recursion is possible here}
> @^recursion@>
> if (cur_cs=0)and@|
> ((cur_chr=so(str_pool[k]))or(cur_chr=so(str_pool[k])-"a"+"A")) then
> begin store_new_token(cur_tok); incr(k);
> end
> else if (cur_cmd<>spacer)or(p<>backup_head) then
> begin back_input;
> if p<>backup_head then back_list(link(backup_head));
> scan_keyword:=false; return;
> end;
> end;
> flush_list(link(backup_head)); scan_keyword:=true;
6801,6802c8157,8169
< @ The next routine `|scan_the|' is used to handle the `\.{\\the}' in
< constructions like `\.{\\the\\month}' and `\.{\\the\\hsize}' and
---
> @ Here is a procedure that sounds an alarm when mu and non-mu units
> are being switched.
>
> @p procedure mu_error;
> begin print_err("Incompatible glue units");
> @.Incompatible glue units@>
> help1("I'm going to assume that 1mu=1pt when they're mixed.");
> error;
> end;
>
> @ The next routine `|scan_something_internal|' is used to fetch internal
> numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}'
> when expanding constructions like `\.{\\the\\toks0}' and
6804,6807c8171,8175
< procedure, which calls |scan_the|; on the other hand, |scan_the| also
< calls |scan_int|, for constructions like `\.{\\the\\chcode\`\\\$}' or
< `\.{\\the\\texinfo 20 3}'. So we have to declare |scan_int| as a
< |forward| procedure.
---
> procedure, which calls |scan_something_internal|; on the other hand,
> |scan_something_internal| also calls |scan_int|, for constructions like
> `\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we
> have to declare |scan_int| as a |forward| procedure. A few other
> procedures are also declared at this point.
6810c8178,8179
< @t\4\4@>@<Declare procedures that scan restricted classes of integers@>
---
> @t\4\4@>@<Declare procedures that scan restricted classes of integers@>@;
> @t\4\4@>@<Declare procedures that scan font-related stuff@>
6812,6815c8181,8183
< @ A single word `\.{\\the}' is used for integers, dimensions, and glue
< speci\-fi\-ca\-tions, so \TeX\ doesn't know exactly what to expect when
< |scan_the| begins. For example, any of the three types could occur
< immediately after `\.{\\hskip\\the}'; and one can even use \.{\\the} with
---
> @ \TeX\ doesn't know exactly what to expect when |scan_something_internal|
> begins. For example, an integer or dimension or glue value could occur
> immediately after `\.{\\hskip}'; and one can even say \.{\\the} with
6818,6830c8186,8201
< allowed after a construction like `\.{\\count\\the}'. To handle the
< various possibilities, |scan_the| has a |level| parameter, which tells the
< ``highest'' kind of quantity that |scan_the| is allowed to produce. Five
< levels are distinguished, namely |int_val|, |dimen_val|, |glue_val|,
< |mu_val|, and |tok_val|.
<
< The output of |scan_the| (and of the other routines |scan_int|, |scan_dimen|,
< and |scan_glue| below) is put into the global variable |cur_val|, and its
< level is put into |cur_val_level|. The highest values of |cur_val_level| are
< special: |mu_val| is used only when |cur_val| points to one of the three
< parameters \.{\\thinmskip}, \.{\\midmskip}, \.{\\thickmskip}, and |tok_val|
< is used only in cases that `\.{\\the\\output}' and `\.{\\the\\everypar}'
< are legitimate.
---
> allowed after a construction like `\.{\\count}'. To handle the various
> possibilities, |scan_something_internal| has a |level| parameter, which
> tells the ``highest'' kind of quantity that |scan_something_internal| is
> allowed to produce. Six levels are distinguished, namely |int_val|,
> |dimen_val|, |glue_val|, |mu_val|, |ident_val|, and |tok_val|.
>
> The output of |scan_something_internal| (and of the other routines
> |scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global
> variable |cur_val|, and its level is put into |cur_val_level|. The highest
> values of |cur_val_level| are special: |mu_val| is used only when
> |cur_val| points to something in a ``muskip'' register, or to one of the
> three parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip};
> |ident_val| is used only when |cur_val| points to a font identifier;
> |tok_val| is used only when |cur_val| points to |null| or to the reference
> count of a token list. The last two cases are allowed only when
> |scan_something_internal| is called with |level=tok_val|.
6834,6835c8205,8206
< reference; if the output is a token list, |cur_val| will point to its
< reference count, but in this case the count will not have been updated.
---
> reference; if the output is a nonempty token list, |cur_val| will point to
> its reference count, but in this case the count will not have been updated.
6842c8213,8214
< @d tok_val=4 {token lists}
---
> @d ident_val=4 {font identifier}
> @d tok_val=5 {token lists}
6848,6854c8220,8223
< @ The hash table is initialized with `\.{\\count}', `\.{\\dimen}', and
< `\.{\\skip}' all having |register| as their command code; they are distinguished
< by the |chr_code|, which is either |int_val|, |dimen_val|, or |glue_val|.
< The `\.{\\hangindent}' primitive is constructed so that |scan_the| can
< treat it exactly like a dimension parameter. The two primitives whose
< command code is `|the|' are different solely because the |equiv| is
< zero for `\.{\\the}' and nonzero for `\.{\\minus}'.
---
> @ The hash table is initialized with `\.{\\count}', `\.{\\dimen}', `\.{\\skip}',
> and `\.{\\muskip}' all having |register| as their command code; they are
> distinguished by the |chr_code|, which is either |int_val|, |dimen_val|,
> |glue_val|, or |mu_val|.
6863,6868c8232,8233
< primitive("hangindent",hang_indent,hanging_indent_code);
< @!@:hangindent_}{\.{\\hangindent} primitive@>
< primitive("the",the,0);
< @!@:the_}{\.{\\the} primitive@>
< primitive("minus",the,1);
< @!@:minus_}{\.{\\minus} primitive@>
---
> primitive("muskip",register,mu_val);
> @!@:mu_skip_}{\.{\\muskip} primitive@>
6872,6879c8237,8245
< else if chr_code=dimen_val then print_esc("dimen")
< else print_esc("skip");
< hang_indent:print_esc("hangindent");
< the: if chr_code=0 then print_esc("the")@+else print_esc("minus");
<
< @ OK, we're ready for |scan_the| itself. A second parameter, |negative|,
< is set |true| if we want the value to be negated (e.g., if `\.{\\minus}'
< has appeared instead of `\.{\\the}').
---
> else if chr_code=dimen_val then print_esc("dimen")
> else if chr_code=glue_val then print_esc("skip")
> else print_esc("muskip");
>
> @ OK, we're ready for |scan_something_internal| itself. A second parameter,
> |negative|, is set |true| if the value that is found should be negated.
> It is assumed that |cur_cmd| and |cur_chr| represent the first token of
> the internal quantity to be scanned; an error will be signalled if
> |cur_cmd<min_internal| or |cur_cmd>max_internal|.
6881,6882c8247,8248
< @d scanned_result_end(#)==cur_val_level←#; end
< @d scanned_result(#)==@+begin cur_val←#;scanned_result_end
---
> @d scanned_result_end(#)==cur_val_level:=#;@+end
> @d scanned_result(#)==@+begin cur_val:=#;scanned_result_end
6884,6886c8250,8251
< @p procedure scan_the(@!level:small_number;@!negative:boolean);
< {fetch an internal parameter}
< label restart;
---
> @p procedure scan_something_internal(@!level:small_number;@!negative:boolean);
> {fetch an internal parameter}
6888c8253,8254
< begin restart: get_nc_token; m←cur_chr;
---
> @!p:0..nest_size; {index into |nest|}
> begin m:=cur_chr;
6891,6895c8257,8262
< assign_toks: @<Fetch a token list, provided that |level=tok_val|@>;
< assign_int: scanned_result(int_par(m))(int_val);
< assign_dimen,hang_indent: scanned_result(dimen_par(m))(dimen_val);
< assign_glue: if m≥thin_mskip then scanned_result(glue_par(m))(mu_val)
< else scanned_result(glue_par(m))(glue_val);
---
> toks_register,assign_toks,def_family,set_font,def_font: @<Fetch a token list or
> font identifier, provided that |level=tok_val|@>;
> assign_int: scanned_result(eqtb[m].int)(int_val);
> assign_dimen: scanned_result(eqtb[m].sc)(dimen_val);
> assign_glue: scanned_result(equiv(m))(glue_val);
> assign_mu_glue: scanned_result(equiv(m))(mu_val);
6897,6898c8264,8271
< set_family: scanned_result(cur_fam)(int_val);
< assign_tex_info: @<Fetch a font parameter@>;
---
> set_prev_graf: @<Fetch the |prev_graf|@>;
> set_page_int:@<Fetch the |dead_cycles| or the |insert_penalties|@>;
> set_page_dimen: @<Fetch something on the |page_so_far|@>;
> set_shape: @<Fetch the |par_shape| size@>;
> set_box_dimen: @<Fetch a box dimension@>;
> char_given,math_given: scanned_result(cur_chr)(int_val);
> assign_font_dimen: @<Fetch a font dimension@>;
> assign_font_int: @<Fetch a font integer@>;
6900,6905c8273
< last_skip: @<Fetch the glue in the current node, if any@>;
< set_font: scanned_result(font_code[cur_font])(int_val);
< def_family: @<Fetch a math font code@>;
< the: begin if m>0 then negative←¬ negative;
< goto restart;
< end;
---
> last_item: @<Fetch an item in the current node, if appropriate@>;
6913,6914c8281,8283
< begin scan_seven_bit_int;
< if m<del_code_base then scanned_result(equiv(m+cur_val))(int_val)
---
> begin scan_char_num;
> if m=math_code_base then scanned_result(ho(math_code(cur_val)))(int_val)
> else if m<math_code_base then scanned_result(equiv(m+cur_val))(int_val)
6918,6925c8287,8314
< @ @<Fetch a token list, provided that |level=tok_val|@>=
< if (level≠tok_val)∨ negative then
< begin print_nl("! Improper use of \the");
< @.Improper use of \\the@>
< help1("I'm forgetting what you said and using zero for this \the.");
< back_error; scanned_result(0)(dimen_val);
< end
< else scanned_result(equiv(m))(tok_val)
---
> @ @<Fetch a token list...@>=
> if level<>tok_val then
> begin print_err("Missing number, treated as zero");
> @.Missing number...@>
> help3("A number should have been here; I inserted `0'.")@/
> ("(If you can't figure out why I needed to see a number,")@/
> ("look up `weird error' in the index to The TeXbook.)");
> @:TeXbook}{\sl The \TeX book@>
> back_error; scanned_result(0)(dimen_val);
> end
> else if cur_cmd<=assign_toks then
> begin if cur_cmd<assign_toks then {|cur_cmd=toks_register|}
> begin scan_eight_bit_int; m:=toks_base+cur_val;
> end;
> scanned_result(equiv(m))(tok_val);
> end
> else begin back_input; scan_font_ident;
> scanned_result(font_id_base+cur_val)(ident_val);
> end
>
> @ Users refer to `\.{\\the\\spacefactor}' only in horizontal
> mode, and to `\.{\\the\\prevdepth}' only in vertical mode; so we put the
> associated mode in the modifier part of the |set_aux| command.
> The |set_page_int| command has modifier 0 or 1, for `\.{\\deadcycles}' and
> `\.{\\insertpenalties}', respectively. The |set_box_dimen| command is
> modified by either |width_offset|, |height_offset|, or |depth_offset|.
> And the |last_item| command is modified by either |int_val|, |dimen_val|,
> |glue_val|, |input_line_no_code|, or |badness_code|.
6927,6928c8316,8317
< @ A user is allowed to refer to `\.{\\the\\spacefactor}' only in horizontal
< mode, and to `\.{\\the\\prevdepth}' only in vertical mode.
---
> @d input_line_no_code=glue_val+1 {code for \.{\\inputlineno}}
> @d badness_code=glue_val+2 {code for \.{\\badness}}
6933c8322
< primitive("prevdepth",set_aux,vmode);
---
> primitive("prevdepth",set_aux,vmode);@/
6934a8324,8343
> primitive("deadcycles",set_page_int,0);
> @!@:dead_cycles_}{\.{\\deadcycles} primitive@>
> primitive("insertpenalties",set_page_int,1);
> @!@:insert_penalties_}{\.{\\insertpenalties} primitive@>
> primitive("wd",set_box_dimen,width_offset);
> @!@:wd_}{\.{\\wd} primitive@>
> primitive("ht",set_box_dimen,height_offset);
> @!@:ht_}{\.{\\ht} primitive@>
> primitive("dp",set_box_dimen,depth_offset);
> @!@:dp_}{\.{\\dp} primitive@>
> primitive("lastpenalty",last_item,int_val);
> @!@:last_penalty_}{\.{\\lastpenalty} primitive@>
> primitive("lastkern",last_item,dimen_val);
> @!@:last_kern_}{\.{\\lastkern} primitive@>
> primitive("lastskip",last_item,glue_val);
> @!@:last_skip_}{\.{\\lastskip} primitive@>
> primitive("inputlineno",last_item,input_line_no_code);
> @!@:input_line_no_}{\.{\\inputlineno} primitive@>
> primitive("badness",last_item,badness_code);
> @!@:badness_}{\.{\\badness} primitive@>
6938c8347,8359
< else print_esc("spacefactor");
---
> @+else print_esc("spacefactor");
> set_page_int: if chr_code=0 then print_esc("deadcycles")
> @+else print_esc("insertpenalties");
> set_box_dimen: if chr_code=width_offset then print_esc("wd")
> else if chr_code=height_offset then print_esc("ht")
> else print_esc("dp");
> last_item: case chr_code of
> int_val: print_esc("lastpenalty");
> dimen_val: print_esc("lastkern");
> glue_val: print_esc("lastskip");
> input_line_no_code: print_esc("inputlineno");
> othercases print_esc("badness")
> endcases;
6941,6965c8362,8385
< if abs(mode)≠m then
< begin print_nl("! Improper use of \the");
< @.Improper use of \\the@>
< help4("You can say \the\spacefactor only in horizontal mode,")@/
< ("and \the\prevdepth only in vertical mode; and")@/
< ("neither of these is meaningful inside \send. So")@/
< ("I'm forgetting what you said and using zero for this \the.");
< back_error; scanned_result(0)(dimen_val);
< end
< else begin cur_val←aux;
< if m=vmode then cur_val_level←dimen_val@+else cur_val_level←int_val;
< end
<
< @ Here is where \.{\\lastskip} is implemented. The reference count will be
< updated later.
< @:last_skip_}{\.{\\lastskip} primitive@>
<
< @<Fetch the glue in the current node, if any@>=
< begin cur_val←zero_glue;
< if not is_char_node(tail)∧(mode≠0) then
< begin if type(tail)=glue_node then cur_val←glue_ptr(tail);
< end
< else if (mode=vmode)∧(tail=head)∧(last_page_glue≠max_halfword) then
< cur_val←last_page_glue;
< cur_val_level←glue_val;
---
> if abs(mode)<>m then
> begin print_err("Improper "); print_cmd_chr(set_aux,m);
> @.Improper \\spacefactor@>
> @.Improper \\prevdepth@>
> help4("You can refer to \spacefactor only in horizontal mode;")@/
> ("you can refer to \prevdepth only in vertical mode; and")@/
> ("neither of these is meaningful inside \write. So")@/
> ("I'm forgetting what you said and using zero instead.");
> error;
> if level<>tok_val then scanned_result(0)(dimen_val)
> else scanned_result(0)(int_val);
> end
> else if m=vmode then scanned_result(prev_depth)(dimen_val)
> else scanned_result(space_factor)(int_val)
>
> @ @<Fetch the |dead_cycles| or the |insert_penalties|@>=
> begin if m=0 then cur_val:=dead_cycles@+else cur_val:=insert_penalties;
> cur_val_level:=int_val;
> end
>
> @ @<Fetch a box dimension@>=
> begin scan_eight_bit_int;
> if box(cur_val)=null then cur_val:=0 @+else cur_val:=mem[box(cur_val)+m].sc;
> cur_val_level:=dimen_val;
6968,6969c8388,8445
< @ @<Fetch a font parameter@>=
< begin scan_tex_info(false); font_info[fmem_ptr].sc←0;
---
> @ Inside an \.{\\output} routine, a user may wish to look at the page totals
> that were present at the moment when output was triggered.
>
> @d max_dimen==@'7777777777 {$2^{30}-1$}
>
> @<Fetch something on the |page_so_far|@>=
> begin if (page_contents=empty) and (not output_active) then
> if m=0 then cur_val:=max_dimen@+else cur_val:=0
> else cur_val:=page_so_far[m];
> cur_val_level:=dimen_val;
> end
>
> @ @<Fetch the |prev_graf|@>=
> if mode=0 then scanned_result(0)(int_val) {|prev_graf=0| within \.{\\write}}
> else begin nest[nest_ptr]:=cur_list; p:=nest_ptr;
> while abs(nest[p].mode_field)<>vmode do decr(p);
> scanned_result(nest[p].pg_field)(int_val);
> end
>
> @ @<Fetch the |par_shape| size@>=
> begin if par_shape_ptr=null then cur_val:=0
> else cur_val:=info(par_shape_ptr);
> cur_val_level:=int_val;
> end
>
> @ Here is where \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\lastskip} are
> implemented. The reference count for \.{\\lastskip} will be updated later.
>
> We also handle \.{\\inputlineno} and \.{\\badness} here, because they are
> legal in similar contexts.
>
> @<Fetch an item in the current node...@>=
> if cur_chr>glue_val then
> begin if cur_chr=input_line_no_code then cur_val:=line
> else cur_val:=last_badness; {|cur_chr=badness_code|}
> cur_val_level:=int_val;
> end
> else begin if cur_chr=glue_val then cur_val:=zero_glue@+else cur_val:=0;
> cur_val_level:=cur_chr;
> if not is_char_node(tail)and(mode<>0) then
> case cur_chr of
> int_val: if type(tail)=penalty_node then cur_val:=penalty(tail);
> dimen_val: if type(tail)=kern_node then cur_val:=width(tail);
> glue_val: if type(tail)=glue_node then
> begin cur_val:=glue_ptr(tail);
> if subtype(tail)=mu_glue then cur_val_level:=mu_val;
> end;
> end {there are no other cases}
> else if (mode=vmode)and(tail=head) then
> case cur_chr of
> int_val: cur_val:=last_penalty;
> dimen_val: cur_val:=last_kern;
> glue_val: if last_glue<>max_halfword then cur_val:=last_glue;
> end; {there are no other cases}
> end
>
> @ @<Fetch a font dimension@>=
> begin find_font_dimen(false); font_info[fmem_ptr].sc:=0;
6972a8449,8454
> @ @<Fetch a font integer@>=
> begin scan_font_ident;
> if m=0 then scanned_result(hyphen_char[cur_val])(int_val)
> else scanned_result(skew_char[cur_val])(int_val);
> end
>
6976,6978c8458,8461
< int_val:cur_val←count(cur_val);
< dimen_val:cur_val←dimen(cur_val);
< glue_val:cur_val←skip(cur_val);
---
> int_val:cur_val:=count(cur_val);
> dimen_val:cur_val:=dimen(cur_val);
> glue_val: cur_val:=skip(cur_val);
> mu_val: cur_val:=mu_skip(cur_val);
6980,6984c8463
< cur_val_level←m;
< end
<
< @ @<Fetch a math font code@>=
< begin scan_four_bit_int; scanned_result(font_code[equiv(m+cur_val)])(int_val);
---
> cur_val_level:=m;
6988,6992c8467,8473
< begin print_nl("! You can't use "); print_cmd_chr(cur_cmd,cur_chr);
< @.You can't use x after \\the@>
< print(" after \the");
< help1("I'm forgetting what you said and using zero for this \the.");
< back_error; scanned_result(0)(dimen_val);
---
> begin print_err("You can't use `"); print_cmd_chr(cur_cmd,cur_chr);
> @.You can't use x after ...@>
> print("' after "); print_esc("the");
> help1("I'm forgetting what you said and using zero instead.");
> error;
> if level<>tok_val then scanned_result(0)(dimen_val)
> else scanned_result(0)(int_val);
7002c8483,8484
< begin if cur_val_level=glue_val then cur_val←width(cur_val);
---
> begin if cur_val_level=glue_val then cur_val:=width(cur_val)
> else if cur_val_level=mu_val then mu_error;
7007a8490
> If |negative| is |true|, |cur_val_level| is known to be |<=mu_val|.
7011,7017c8494,8506
< if cur_val_level≥glue_val then
< begin cur_val←new_spec(cur_val);
< @<Negate all three glue components of |cur_val|@>;
< end
< else cur_val←-cur_val
< else if (cur_val_level≥glue_val)∧(cur_val_level≠tok_val) then
< add_glue_ref(cur_val)
---
> if cur_val_level>=glue_val then
> begin cur_val:=new_spec(cur_val);
> @<Negate all three glue components of |cur_val|@>;
> end
> else negate(cur_val)
> else if (cur_val_level>=glue_val)and(cur_val_level<=mu_val) then
> add_glue_ref(cur_val)
>
> @ @<Negate all three...@>=
> begin negate(width(cur_val));
> negate(stretch(cur_val));
> negate(shrink(cur_val));
> end
7021,7032c8510,8511
< applications of |scan_int| that have already been made inside of |scan_the|:
<
< @<Declare procedures that scan restricted classes of integers@>=
< procedure scan_seven_bit_int;
< begin scan_int;
< if (cur_val<0)∨(cur_val>127) then
< begin print_nl("! Bad character code");
< @.Bad character code@>
< help2("The numeric code for a character must be between 0 and 127.")@/
< ("I changed this one to zero."); int_error(cur_val); cur_val←0;
< end;
< end;
---
> applications of |scan_int| that have already been made inside of
> |scan_something_internal|.
7037,7038c8516,8517
< if (cur_val<0)∨(cur_val>255) then
< begin print_nl("! Bad register code");
---
> if (cur_val<0)or(cur_val>255) then
> begin print_err("Bad register code");
7040,7042c8519,8521
< help2("Boxes, counts, dimens, and skips must be between 0 and 255.")@/
< ("I changed this one to zero."); int_error(cur_val); cur_val←0;
< end;
---
> help2("A register number must be between 0 and 255.")@/
> ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
> end;
7046c8525
< procedure scan_four_bit_int;
---
> procedure scan_char_num;
7048,7053c8527,8532
< if (cur_val<0)∨(cur_val>15) then
< begin print_nl("! Bad number");
< @.Bad number@>
< help2("Since I expected to read a number between 0 and 15,")@/
< ("I changed this one to zero."); int_error(cur_val); cur_val←0;
< end;
---
> if (cur_val<0)or(cur_val>255) then
> begin print_err("Bad character code");
> @.Bad character code@>
> help2("A character number must be between 0 and 255.")@/
> ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
> end;
7060c8539
< procedure scan_char_num;
---
> procedure scan_four_bit_int;
7062,7067c8541,8546
< if (cur_val<0)∨(cur_val>255) then
< begin print_nl("! Bad \char code");
< @.Bad \\char code@>
< help2("The numeric code following \char must be between 0 and 255.")@/
< ("I changed this one to zero."); int_error(cur_val); cur_val←0;
< end;
---
> if (cur_val<0)or(cur_val>15) then
> begin print_err("Bad number");
> @.Bad number@>
> help2("Since I expected to read a number between 0 and 15,")@/
> ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
> end;
7073,7078c8552,8557
< if (cur_val<0)∨(cur_val>@'77777) then
< begin print_nl("! Bad \mathchar code");
< @.Bad \\mathchar code@>
< help2("A numeric \mathchar code must be between 0 and 32767.")@/
< ("I changed this one to zero."); int_error(cur_val); cur_val←0;
< end;
---
> if (cur_val<0)or(cur_val>@'77777) then
> begin print_err("Bad mathchar");
> @.Bad mathchar@>
> help2("A mathchar number must be between 0 and 32767.")@/
> ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
> end;
7084,7085c8563,8564
< if (cur_val<0)∨(cur_val>@'777777777) then
< begin print_nl("! Bad delimiter code");
---
> if (cur_val<0)or(cur_val>@'777777777) then
> begin print_err("Bad delimiter code");
7087,7089c8566,8568
< help2("A numeric delimiter code must be between 0 and 2^{27}-1.")@/
< ("I changed this one to zero."); int_error(cur_val); cur_val←0;
< end;
---
> help2("A numeric delimiter code must be between 0 and 2^{27}-1.")@/
> ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
> end;
7094,7097c8573,8575
< octal constant (i.e., radix 8, preceded by@@\.\'), a hexadecimal constant
< (radix 16, preceded by@@\."), an alphabetic constant (preceded by@@\.\`), or
< an internal variable obtained by prefixing \.{\\the}. (The `\.{\\the}'
< can be omitted before `\.{\\count}'.) After scanning is complete,
---
> octal constant (i.e., radix 8, preceded by~\.\'), a hexadecimal constant
> (radix 16, preceded by~\."), an alphabetic constant (preceded by~\.\`), or
> an internal variable. After scanning is complete,
7101c8579
< otherwise |radix| is set to zero. An optional space follows the number.
---
> otherwise |radix| is set to zero. An optional space follows a constant.
7103,7106d8580
< @d plus_token=other_token+"+" {plus sign}
< @d minus_token=other_token+"-" {minus sign}
< @d zero_token=other_token+"0" {zero, the smallest digit}
< @d A_token=letter_token+"A" {the smallest special hex digit}
7110a8585
> @d continental_point_token=other_token+"," {decimal point, Eurostyle}
7114a8590,8596
> @ We initialize the following global variables just in case |expand|
> comes into action before any of the basic scanning routines has assigned
> them a value.
>
> @<Set init...@>=
> cur_val:=0; cur_val_level:=int_val; radix:=0; cur_order:=normal;
>
7128c8610
< begin radix←0; OK_so_far←true;@/
---
> begin radix:=0; OK_so_far:=true;@/
7131c8613,8614
< else if (cur_cmd=the)∨(cur_cmd=register) then @<Fetch an internal integer@>
---
> else if (cur_cmd>=min_internal)and(cur_cmd<=max_internal) then
> scan_something_internal(int_val,false)
7133c8616
< if negative then cur_val←-cur_val;
---
> if negative then negate(cur_val);
7137c8620
< negative←false;
---
> negative:=false;
7139,7142c8622,8625
< if cur_tok=minus_token then
< begin negative ← ¬ negative; cur_tok←plus_token;
< end;
< until cur_tok≠plus_token
---
> if cur_tok=other_token+"-" then
> begin negative := not negative; cur_tok:=other_token+"+";
> end;
> until cur_tok<>other_token+"+"
7149,7154c8632,8642
< if cur_tok<cs_token_flag then cur_val←cur_chr
< else if cur_tok<cs_token_flag+active_base then
< cur_val←cur_tok-cs_token_flag-single_base
< else cur_val←cur_tok-cs_token_flag-active_base;
< if cur_val>127 then
< begin print_nl("! Improper alphabetic constant");
---
> if cur_tok<cs_token_flag then
> begin cur_val:=cur_chr;
> if cur_cmd<=right_brace then
> if cur_cmd=right_brace then incr(align_state)
> else decr(align_state);
> end
> else if cur_tok<cs_token_flag+single_base then
> cur_val:=cur_tok-cs_token_flag-active_base
> else cur_val:=cur_tok-cs_token_flag-single_base;
> if cur_val>255 then
> begin print_err("Improper alphabetic constant");
7156,7159c8644,8647
< help2("A one-character control sequence belongs after a ` mark.")@/
< ("So I'm essentially inserting \0 here.");
< cur_val←"0"; back_error;
< end
---
> help2("A one-character control sequence belongs after a ` mark.")@/
> ("So I'm essentially inserting \0 here.");
> cur_val:="0"; back_error;
> end
7163,7166c8651,8652
< @ @<Fetch an internal integer@>=
< begin if cur_cmd=register then back_input {implied `\.{\\the}'}
< else if cur_chr≠0 then negative ← ¬ negative; {handle `\.{\\minus}'}
< scan_the(int_val,false);
---
> @ @<Scan an optional space@>=
> begin get_x_token; if cur_cmd<>spacer then back_input;
7170c8656
< begin radix←10; m←214748364;
---
> begin radix:=10; m:=214748364;
7172,7173c8658,8659
< begin radix←8; m←@'2000000000; get_nc_token;
< end
---
> begin radix:=8; m:=@'2000000000; get_x_token;
> end
7175,7177c8661,8663
< begin radix←16; m←@'1000000000; get_nc_token;
< end;
< vacuous←true; cur_val←0;@/
---
> begin radix:=16; m:=@'1000000000; get_x_token;
> end;
> vacuous:=true; cur_val:=0;@/
7179,7180c8665,8666
< if vacuous then @<Express astonishment that no number was here@>;
< if cur_cmd≠spacer then back_input;
---
> if vacuous then @<Express astonishment that no number was here@>
> else if cur_cmd<>spacer then back_input;
7183a8670,8672
> @d zero_token=other_token+"0" {zero, the smallest digit}
> @d A_token=letter_token+"A" {the smallest special hex digit}
> @d other_A_token=other_token+"A" {special hex digit of type |other_char|}
7186,7194c8675,8686
< loop@+ begin if (cur_tok<zero_token+radix)∧(cur_tok≥zero_token)∧
< (cur_tok≤zero_token+9) then d←cur_tok-zero_token
< else if (radix=16)∧(cur_tok≤A_token+5)∧(cur_tok≥A_token) then
< d←cur_tok-A_token+10
< else goto done;
< vacuous←false;
< if (cur_val≥m)∧((cur_val>m)∨(d>7)∨(radix≠10)) then
< begin if OK_so_far then
< begin print_nl("! Number too big");
---
> loop@+ begin if (cur_tok<zero_token+radix)and(cur_tok>=zero_token)and
> (cur_tok<=zero_token+9) then d:=cur_tok-zero_token
> else if radix=16 then
> if (cur_tok<=A_token+5)and(cur_tok>=A_token) then d:=cur_tok-A_token+10
> else if (cur_tok<=other_A_token+5)and(cur_tok>=other_A_token) then
> d:=cur_tok-other_A_token+10
> else goto done
> else goto done;
> vacuous:=false;
> if (cur_val>=m)and((cur_val>m)or(d>7)or(radix<>10)) then
> begin if OK_so_far then
> begin print_err("Number too big");
7196,7203c8688,8695
< help2("I can only go up to 2147483647='17777777777=""7FFFFFFF,")@/
< ("so I'm using that number instead of yours.");
< error; cur_val←infinity; OK_so_far←false;
< end;
< end
< else cur_val←cur_val*radix+d;
< get_nc_token;
< end;
---
> help2("I can only go up to 2147483647='17777777777=""7FFFFFFF,")@/
> ("so I'm using that number instead of yours.");
> error; cur_val:=infinity; OK_so_far:=false;
> end;
> end
> else cur_val:=cur_val*radix+d;
> get_x_token;
> end;
7207,7208c8699,8700
< begin print_nl("! Missing number");
< @.Missing number@>
---
> begin print_err("Missing number, treated as zero");
> @.Missing number...@>
7210,7212c8702,8705
< ("(If you can't figure out why I needed to see a number,")@/
< ("look up `weird error' in the TeX manual index.)");
< error;
---
> ("(If you can't figure out why I needed to see a number,")@/
> ("look up `weird error' in the index to The TeXbook.)");
> @:TeXbook}{\sl The \TeX book@>
> back_error;
7243a8737
> @d scan_normal_dimen==scan_dimen(false,false,false)
7246,7247c8740,8741
< {sets |cur_val| to a dimension}
< label done, found, not_found, attach_fraction, attach_sign;
---
> {sets |cur_val| to a dimension}
> label done, done1, done2, found, not_found, attach_fraction, attach_sign;
7251c8745
< begin f←0; arith_error←false; cur_order←normal; negative←false;
---
> begin f:=0; arith_error:=false; cur_order:=normal; negative:=false;
7253,7266c8747,8762
< begin @<Get the next non-blank non-sign...@>;
< if (cur_cmd=the)∨(cur_cmd=register) then
< @<Fetch an internal dimension and |goto attach_sign|,
< or fetch an internal integer@>
< else begin back_input;
< if cur_tok≠point_token then scan_int
< else begin radix←10; cur_val←0;
< end;
< if (radix=10)∧(cur_tok=point_token) then @<Scan decimal fraction@>;
< end;
< end;
< if cur_val<0 then {in this case |f<0|}
< begin negative ← ¬ negative; cur_val←-cur_val;
< end;
---
> begin @<Get the next non-blank non-sign...@>;
> if (cur_cmd>=min_internal)and(cur_cmd<=max_internal) then
> @<Fetch an internal dimension and |goto attach_sign|,
> or fetch an internal integer@>
> else begin back_input;
> if cur_tok=continental_point_token then cur_tok:=point_token;
> if cur_tok<>point_token then scan_int
> else begin radix:=10; cur_val:=0;
> end;
> if cur_tok=continental_point_token then cur_tok:=point_token;
> if (radix=10)and(cur_tok=point_token) then @<Scan decimal fraction@>;
> end;
> end;
> if cur_val<0 then {in this case |f=0|}
> begin negative := not negative; negate(cur_val);
> end;
7268,7271c8764
< are $x$ units per sp@>;
< attach_sign: if negative then cur_val←-cur_val;
< if arith_error ∨(abs(cur_val)≥@'10000000000) then
< @<Report that this dimension is out of range@>;
---
> are |x| sp per unit; |goto attach_sign| if the units are internal@>;
7272a8766,8768
> attach_sign: if arith_error or(abs(cur_val)>=@'10000000000) then
> @<Report that this dimension is out of range@>;
> if negative then negate(cur_val);
7276,7280c8772,8780
< begin if cur_cmd=register then back_input {implied `\.{\\the}'}
< else if cur_chr≠0 then negative ← ¬ negative; {handle `\.{\\minus}'}
< scan_the(dimen_val,false);
< if cur_val_level=dimen_val then goto attach_sign;
< end
---
> if mu then
> begin scan_something_internal(mu_val,false);
> @<Coerce glue to a dimension@>;
> if cur_val_level=mu_val then goto attach_sign;
> if cur_val_level<>int_val then mu_error;
> end
> else begin scan_something_internal(dimen_val,false);
> if cur_val_level=dimen_val then goto attach_sign;
> end
7284,7285c8784,8785
< @!k:small_number; {number of digits in a decimal fraction}
< @!j:small_number; {index into a box node}
---
> @!k,@!kk:small_number; {number of digits in a decimal fraction}
> @!p,@!q:pointer; {top of decimal digit stack}
7287c8787,8796
< @!save_cur_val:scaled; {temporary storage of |cur_val|}
---
> @!save_cur_val:integer; {temporary storage of |cur_val|}
>
> @ The following code is executed when |scan_something_internal| was
> called asking for |mu_val|, when we really wanted a ``mudimen'' instead
> of ``muglue.''
>
> @<Coerce glue to a dimension@>=
> if cur_val_level>=glue_val then
> begin v:=width(cur_val); delete_glue_ref(cur_val); cur_val:=v;
> end
7296,7303c8805,8817
< begin k←0; get_token; {|point_token| is being re-scanned}
< loop@+ begin get_nc_token;
< if (cur_tok>zero_token+9)∨(cur_tok<zero_token) then goto done;
< if k<16 then {digits for |k≥16| cannot affect the result}
< begin dig[k]←cur_tok-zero_token; incr(k);
< end;
< end;
< done: f←round_decimals(k); back_input;
---
> begin k:=0; p:=null; get_token; {|point_token| is being re-scanned}
> loop@+ begin get_x_token;
> if (cur_tok>zero_token+9)or(cur_tok<zero_token) then goto done1;
> if k<17 then {digits for |k>=17| cannot affect the result}
> begin q:=get_avail; link(q):=p; info(q):=cur_tok-zero_token;
> p:=q; incr(k);
> end;
> end;
> done1: for kk:=k downto 1 do
> begin dig[kk-1]:=info(p); q:=p; p:=link(p); free_avail(q);
> end;
> f:=round_decimals(k);
> if cur_cmd<>spacer then back_input;
7311,7312c8825
< does not overflow. This section of the program is followed by the label
< |attach_sign|.
---
> does not overflow.
7315,7318c8828,8831
< if inf then @<Scan for \.{fil} units; |goto attach_fraction| if found@>;
< if mu then @<Scan for \.{mu} units and |goto attach_fraction|@>;
< @<Scan for units that are internal dimensions,
< and |goto attach_sign| if found@>;
---
> if inf then @<Scan for \(f)\.{fil} units; |goto attach_fraction| if found@>;
> @<Scan for \(u)units that are internal dimensions;
> |goto attach_sign| with |cur_val| set if found@>;
> if mu then @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>;
7319a8833
> @.true@>
7321,7323c8835,8840
< @<Scan for all other units and adjust |cur_val| and |f| accordingly@>;
< attach_fraction: if cur_val≥@'40000 then arith_error←true
< else cur_val←cur_val*unity+f
---
> @.pt@>
> @<Scan for \(a)all other units and adjust |cur_val| and |f| accordingly;
> |goto done| in the case of scaled points@>;
> attach_fraction: if cur_val>=@'40000 then arith_error:=true
> else cur_val:=cur_val*unity+f;
> done:
7325c8842,8845
< @ @<Scan for \.{fil} units...@>=
---
> @ A specification like `\.{filllll}' or `\.{fill L L L}' will lead to two
> error messages (one for each additional keyword \.{"l"}).
>
> @<Scan for \(f)\.{fil} units...@>=
7327,7331c8847,8851
< begin cur_order←fil;
< while scan_keyword("l") do
< begin if cur_order=filll then
< begin help1("I dddon't go any higher than filll.");
< print_nl("! Illegal unit of measure (");
---
> @.fil@>
> begin cur_order:=fil;
> while scan_keyword("l") do
> begin if cur_order=filll then
> begin print_err("Illegal unit of measure (");
7333,7338c8853,8859
< print("replaced by filll)"); error;
< end
< else incr(cur_order);
< end;
< goto attach_fraction;
< end
---
> print("replaced by filll)");
> help1("I dddon't go any higher than filll."); error;
> end
> else incr(cur_order);
> end;
> goto attach_fraction;
> end
7340,7360c8861,8876
< @ @<Scan for \.{mu} units and |goto attach_fraction|@>=
< if scan_keyword("mu") then goto attach_fraction
< else begin print_nl("! Illegal unit of measure ("); print("mu inserted)");
< @.Illegal unit of measure@>
< help4("The unit of measurement in \mskip glue must be mu.")@/
< ("To recover gracefully from this error, it's best to")@/
< ("delete the erroneous units; e.g., type `2' to delete")@/
< ("two letters. (See Chapter 27 of the manual.)");
< error; goto attach_fraction;
< end
<
< @ @d set_internal_dimen(#)==begin v←#; goto found;
< end
<
< @<Scan for units that are internal dimensions...@>=
< if scan_keyword("em") then set_internal_dimen(@<The em width for |cur_font|@>);
< if scan_keyword("ex") then set_internal_dimen(@<The x-height for |cur_font|@>);
< if scan_keyword("vu") then set_internal_dimen(var_unit);
< if scan_keyword("wd") then j←width_offset
< else if scan_keyword("dp") then j←depth_offset
< else if scan_keyword("ht") then j←height_offset
---
> @ @<Scan for \(u)units that are internal dimensions...@>=
> save_cur_val:=cur_val;
> @<Get the next non-blank non-call...@>;
> if (cur_cmd<min_internal)or(cur_cmd>max_internal) then back_input
> else begin if mu then
> begin scan_something_internal(mu_val,false); @<Coerce glue...@>;
> if cur_val_level<>mu_val then mu_error;
> end
> else scan_something_internal(dimen_val,false);
> v:=cur_val; goto found;
> end;
> if mu then goto not_found;
> if scan_keyword("em") then v:=(@<The em width for |cur_font|@>)
> @.em@>
> else if scan_keyword("ex") then v:=(@<The x-height for |cur_font|@>)
> @.ex@>
7362,7365c8878,8879
< save_cur_val←cur_val; scan_eight_bit_int;
< if box(cur_val)=null then v←0@+else v←mem[box(cur_val)+j].sc;
< cur_val←save_cur_val;
< found:cur_val←nx_plus_y(cur_val,v,xn_over_d(v,f,@'200000));
---
> @<Scan an optional space@>;
> found:cur_val:=nx_plus_y(save_cur_val,v,xn_over_d(v,f,@'200000));
7369,7377c8883,8894
< @ @<Adjust \(f)for the magnification ratio@>=
< begin @<Scan an optional space@>;
< prepare_mag;
< if mag≠1000 then
< begin cur_val←xn_over_d(cur_val,1000,mag);
< f←(1000*f+@'200000*remainder) div mag;
< cur_val←cur_val+(f div @'200000); f←f mod @'200000;
< end;
< end
---
> @ @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>=
> if scan_keyword("mu") then goto attach_fraction
> @.mu@>
> else begin print_err("Illegal unit of measure ("); print("mu inserted)");
> @.Illegal unit of measure@>
> help4("The unit of measurement in math glue must be mu.")@/
> ("To recover gracefully from this error, it's best to")@/
> ("delete the erroneous units; e.g., type `2' to delete")@/
> ("two letters. (See Chapter 27 of The TeXbook.)");
> @:TeXbook}{\sl The \TeX book@>
> error; goto attach_fraction;
> end
7379,7380c8896,8910
< @ All of the necessary conversion factors can be specified exactly as a
< fraction whose numerator and denominator are 65536 or less.
---
> @ @<Adjust \(f)for the magnification ratio@>=
> begin prepare_mag;
> if mag<>1000 then
> begin cur_val:=xn_over_d(cur_val,1000,mag);
> f:=(1000*f+@'200000*remainder) div mag;
> cur_val:=cur_val+(f div @'200000); f:=f mod @'200000;
> end;
> end
>
> @ The necessary conversion factors can all be specified exactly as
> fractions whose numerator and denominator sum to 32768 or less.
> According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$;
> this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard
> @^Bosshard, Hans Rudolf@>
> in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980).
7382,7383c8912,8913
< @d set_conversion_end(#)== denom←#; end
< @d set_conversion(#)==@+begin num←#; set_conversion_end
---
> @d set_conversion_end(#)== denom:=#; end
> @d set_conversion(#)==@+begin num:=#; set_conversion_end
7385c8915
< @<Scan for all other units and adjust |cur_val| and |f| accordingly@>=
---
> @<Scan for \(a)all other units and adjust |cur_val| and |f|...@>=
7386a8917
> @.in@>
7387a8919
> @.pc@>
7388a8921
> @.cm@>
7389a8923
> @.mm@>
7391,7397c8925,8936
< else if scan_keyword("dd") then set_conversion(215)(201)
< else if scan_keyword("cc") then set_conversion(2580)(201)
< else if scan_keyword("sp") then goto attach_sign
< else @<Complain about unknown unit and |goto attach_fraction|@>;
< cur_val←xn_over_d(cur_val,num,denom);
< f←(num*f+@'200000*remainder) div denom;@/
< cur_val←cur_val+(f div @'200000); f←f mod @'200000
---
> @.bp@>
> else if scan_keyword("dd") then set_conversion(1238)(1157)
> @.dd@>
> else if scan_keyword("cc") then set_conversion(14856)(1157)
> @.cc@>
> else if scan_keyword("sp") then goto done
> @.sp@>
> else @<Complain about unknown unit and |goto done2|@>;
> cur_val:=xn_over_d(cur_val,num,denom);
> f:=(num*f+@'200000*remainder) div denom;@/
> cur_val:=cur_val+(f div @'200000); f:=f mod @'200000;
> done2:
7400c8939
< begin print_nl("! Illegal unit of measure ("); print("pt inserted)");
---
> begin print_err("Illegal unit of measure ("); print("pt inserted)");
7402,7408c8941,8948
< help6("Dimensions can be in units of em, ex, vu, wd, dp, ht, in,")@/
< ("pt, pc, cm, mm, bp, dd, cc, or sp, but yours is a new one.")@/
< ("I'll assume that you meant to say pt, for printers' points.")@/
< ("To recover gracefully from this error, it's best to")@/
< ("delete the erroneous units; e.g., type `2' to delete")@/
< ("two letters. (See Chapter 27 of the manual.)");
< error; goto attach_fraction;
---
> help6("Dimensions can be in units of em, ex, in, pt, pc,")@/
> ("cm, mm, dd, cc, bp, or sp; but yours is a new one!")@/
> ("I'll assume that you meant to say pt, for printer's points.")@/
> ("To recover gracefully from this error, it's best to")@/
> ("delete the erroneous units; e.g., type `2' to delete")@/
> ("two letters. (See Chapter 27 of The TeXbook.)");
> @:TeXbook}{\sl The \TeX book@>
> error; goto done2;
7411d8950
< @ @d max_dimen==@'7777777777 {$2^{30}-1$}
7413,7414c8952,8953
< @<Report that this dimension is out of range@>=
< begin print_nl("! Dimension too large");
---
> @ @<Report that this dimension is out of range@>=
> begin print_err("Dimension too large");
7417,7418c8956,8957
< ("Continue and I'll use the largest value I can.");@/
< error; cur_val←max_dimen;
---
> ("Continue and I'll use the largest value I can.");@/
> error; cur_val:=max_dimen; arith_error:=false;
7423c8962
< glue spec will take account of the fact that |cur_val| is pointing to it.
---
> glue spec will take account of the fact that |cur_val| is pointing to~it.
7425,7426c8964
< As before, the |mu| parameter is |true| if the glue is supposed to be
< an \.{\\mskip}.
---
> The |level| parameter should be either |glue_val| or |mu_val|.
7430c8968
< most of the work has already been done for us.
---
> most of the work has already been done.
7432c8970,8971
< @p procedure scan_glue(@!mu:boolean); {sets |cur_val| to a glue spec pointer}
---
> @p procedure scan_glue(@!level:small_number);
> {sets |cur_val| to a glue spec pointer}
7436,7445c8975,8988
< begin @<Get the next non-blank non-sign...@>;
< if (cur_cmd=the)∨(cur_cmd=register) then
< begin if cur_cmd=register then back_input {implied `\.{\\the}'}
< else if cur_chr≠0 then negative ← ¬ negative; {handle `\.{\\minus}'}
< scan_the(glue_val,negative); negative←false;
< if cur_val_level=glue_val then return;
< if cur_val_level=int_val then scan_dimen(mu,false,true);
< end
< else begin back_input; scan_dimen(mu,false,false);
< end;
---
> @!mu:boolean; {does |level=mu_val|?}
> begin mu:=(level=mu_val); @<Get the next non-blank non-sign...@>;
> if (cur_cmd>=min_internal)and(cur_cmd<=max_internal) then
> begin scan_something_internal(level,negative);
> if cur_val_level>=glue_val then
> begin if cur_val_level<>level then mu_error;
> return;
> end;
> if cur_val_level=int_val then scan_dimen(mu,false,true)
> else if level=mu_val then mu_error;
> end
> else begin back_input; scan_dimen(mu,false,false);
> if negative then negate(cur_val);
> end;
7447,7448c8990
< stretch and shrink components@>;
< if negative then @<Negate all three glue components of |cur_val|@>;
---
> stretch and shrink components@>;
7452c8994
< q←new_spec(zero_glue); width(q)←cur_val;
---
> q:=new_spec(zero_glue); width(q):=cur_val;
7454,7456c8996,8999
< begin scan_dimen(mu,true,false);
< stretch(q)←cur_val; stretch_order(q)←cur_order;
< end;
---
> @.plus@>
> begin scan_dimen(mu,true,false);
> stretch(q):=cur_val; stretch_order(q):=cur_order;
> end;
7458,7467c9001,9005
< begin scan_dimen(mu,true,false);
< shrink(q)←cur_val; shrink_order(q)←cur_order;
< end;
< cur_val←q
<
< @ @<Negate all three...@>=
< begin width(cur_val)←-width(cur_val);
< stretch(cur_val)←-stretch(cur_val);
< shrink(cur_val)←-shrink(cur_val);
< end
---
> @.minus@>
> begin scan_dimen(mu,true,false);
> shrink(q):=cur_val; shrink_order(q):=cur_order;
> end;
> cur_val:=q
7476c9014
< @d default_rule=26215 {.40001 pt}
---
> @d default_rule=26214 {0.4\thinspace pt}
7481,7484c9019,9022
< begin q←new_rule; {|width|, |depth|, and |height| all equal |null_flag| now}
< if cur_cmd=vrule then width(q)←default_rule
< else begin height(q)←default_rule; depth(q)←0;
< end;
---
> begin q:=new_rule; {|width|, |depth|, and |height| all equal |null_flag| now}
> if cur_cmd=vrule then width(q):=default_rule
> else begin height(q):=default_rule; depth(q):=0;
> end;
7486,7487c9024,9026
< begin scan_dimen(false,false,false); width(q)←cur_val; goto reswitch;
< end;
---
> @.width@>
> begin scan_normal_dimen; width(q):=cur_val; goto reswitch;
> end;
7489,7490c9028,9030
< begin scan_dimen(false,false,false); height(q)←cur_val; goto reswitch;
< end;
---
> @.height@>
> begin scan_normal_dimen; height(q):=cur_val; goto reswitch;
> end;
7492,7494c9032,9035
< begin scan_dimen(false,false,false); depth(q)←cur_val; goto reswitch;
< end;
< scan_rule_spec←q;
---
> @.depth@>
> begin scan_normal_dimen; depth(q):=cur_val; goto reswitch;
> end;
> scan_rule_spec:=q;
7495a9037
>
7498,7506c9040
< and \.{\\send} are produced by a procedure called |scan_toks|.
<
< Before we get into the details of |scan_toks|, let's consider a much simpler
< task, that of converting the current string into a token list. The |str_toks|
< function does this; it classifies spaces as type |spacer|, characters |≥"a"|
< as type |letter|, and everything else as type |other_char|. (These
< three categories are sufficient, since |str_toks| is used only with the
< special strings that can occur when \.{\\the} or \.{\\number} constructions
< are being expanded.)
---
> and \.{\\write} are produced by a procedure called |scan_toks|.
7507a9042,9045
> Before we get into the details of |scan_toks|, let's consider a much
> simpler task, that of converting the current string into a token list.
> The |str_toks| function does this; it classifies spaces as type |spacer|
> and everything else as type |other_char|.
7512c9050,9051
< @p function str_toks:pointer; {changes the current string to a token list}
---
> @p function str_toks(@!b:pool_pointer):pointer;
> {changes the string |str_pool[b..pool_ptr]| to a token list}
7518c9057
< p←temp_head; link(p)←null; k←str_start[str_ptr];
---
> p:=temp_head; link(p):=null; k:=b;
7520,7527c9059,9065
< begin t←str_pool[k];
< if t=" " then t←space_token
< else if t≥"a" then t←letter_token+t
< else t←other_token+t;
< store_new_token(t);
< incr(k);
< end;
< pool_ptr←str_start[str_ptr]; str_toks←p;
---
> begin t:=so(str_pool[k]);
> if t=" " then t:=space_token
> else t:=other_token+t;
> fast_store_new_token(t);
> incr(k);
> end;
> pool_ptr:=b; str_toks:=p;
7531a9070
>
7533,7535c9072,9073
< i.e., whatever can follow `\.{\\the}' or `\.{\\minus}', and it
< constructs a token list containing something like `\.{-3.0pt
< minus 0.5fill}'.
---
> i.e., whatever can follow `\.{\\the}', and it constructs a token list
> containing something like `\.{-3.0pt minus 0.5fill}'.
7537c9075
< @p function the_toks(@!negative:boolean):pointer;
---
> @p function the_toks:pointer;
7540,7553c9078,9092
< begin scan_the(tok_val,negative);
< if cur_val_level=tok_val then @<Copy the token list@>
< else begin old_setting←selector; selector←new_string;
< case cur_val_level of
< int_val:print_int(cur_val);
< dimen_val:begin print_scaled(cur_val); print("pt");
< end;
< glue_val: begin print_spec(cur_val,"pt"); delete_glue_ref(cur_val);
< end;
< mu_val: begin print_spec(cur_val,"mu"); delete_glue_ref(cur_val);
< end;
< end; {there are no other cases}
< selector←old_setting; the_toks←str_toks;
< end;
---
> @!b:pool_pointer; {base of temporary string}
> begin get_x_token; scan_something_internal(tok_val,false);
> if cur_val_level>=ident_val then @<Copy the token list@>
> else begin old_setting:=selector; selector:=new_string; b:=pool_ptr;
> case cur_val_level of
> int_val:print_int(cur_val);
> dimen_val:begin print_scaled(cur_val); print("pt");
> end;
> glue_val: begin print_spec(cur_val,"pt"); delete_glue_ref(cur_val);
> end;
> mu_val: begin print_spec(cur_val,"mu"); delete_glue_ref(cur_val);
> end;
> end; {there are no other cases}
> selector:=old_setting; the_toks:=str_toks(b);
> end;
7557,7562c9096,9135
< begin r←link(cur_val); p←temp_head; link(p)←null;
< while r≠null do
< begin store_new_token(info(r)); r←link(r);
< end;
< the_toks←p;
< end
---
> begin p:=temp_head; link(p):=null;
> if cur_val_level=ident_val then store_new_token(cs_token_flag+cur_val)
> else if cur_val<>null then
> begin r:=link(cur_val); {do not copy the reference count}
> while r<>null do
> begin fast_store_new_token(info(r)); r:=link(r);
> end;
> end;
> the_toks:=p;
> end
>
> @ Here's part of the |expand| subroutine that we are now ready to complete:
>
> @p procedure ins_the_toks;
> begin link(garbage):=the_toks; ins_list(link(temp_head));
> end;
>
> @ The primitives \.{\\number}, \.{\\romannumeral}, \.{\\string}, \.{\\meaning},
> \.{\\fontname}, and \.{\\jobname} are defined as follows.
>
> @d number_code=0 {command code for \.{\\number}}
> @d roman_numeral_code=1 {command code for \.{\\romannumeral}}
> @d string_code=2 {command code for \.{\\string}}
> @d meaning_code=3 {command code for \.{\\meaning}}
> @d font_name_code=4 {command code for \.{\\fontname}}
> @d job_name_code=5 {command code for \.{\\jobname}}
>
> @<Put each...@>=
> primitive("number",convert,number_code);@/
> @!@:number_}{\.{\\number} primitive@>
> primitive("romannumeral",convert,roman_numeral_code);@/
> @!@:roman_numeral_}{\.{\\romannumeral} primitive@>
> primitive("string",convert,string_code);@/
> @!@:string_}{\.{\\string} primitive@>
> primitive("meaning",convert,meaning_code);@/
> @!@:meaning_}{\.{\\meaning} primitive@>
> primitive("fontname",convert,font_name_code);@/
> @!@:font_name_}{\.{\\fontname} primitive@>
> primitive("jobname",convert,job_name_code);@/
> @!@:job_name_}{\.{\\jobname} primitive@>
7564,7565c9137,9149
< @ Similarly, we have |num_toks|, which prepares the token list
< following `\.{\\number}':
---
> @ @<Cases of |print_cmd_chr|...@>=
> convert: case chr_code of
> number_code: print_esc("number");
> roman_numeral_code: print_esc("romannumeral");
> string_code: print_esc("string");
> meaning_code: print_esc("meaning");
> font_name_code: print_esc("fontname");
> othercases print_esc("jobname")
> endcases;
>
> @ The procedure |conv_toks| uses |str_toks| to insert the token list
> for |convert| functions into the scanner; `\.{\\outer}' control sequences
> are allowed to follow `\.{\\string}' and `\.{\\meaning}'.
7567c9151
< @p function num_toks:pointer;
---
> @p procedure conv_toks;
7569,7572c9153,9159
< begin old_setting←selector;
< scan_the(int_val,false); selector←new_string;
< if cur_val≥0 then print_int(cur_val)@+else print_roman_int(-cur_val);
< selector←old_setting; num_toks←str_toks;
---
> @!c:number_code..job_name_code; {desired type of conversion}
> @!save_scanner_status:small_number; {|scanner_status| upon entry}
> @!b:pool_pointer; {base of temporary string}
> begin c:=cur_chr; @<Scan the argument for command |c|@>;
> old_setting:=selector; selector:=new_string; b:=pool_ptr;
> @<Print the result of command |c|@>;
> selector:=old_setting; link(garbage):=str_toks(b); ins_list(link(temp_head));
7574a9162,9187
> @ @<Scan the argument for command |c|@>=
> case c of
> number_code,roman_numeral_code: scan_int;
> string_code, meaning_code: begin save_scanner_status:=scanner_status;
> scanner_status:=normal; get_token; scanner_status:=save_scanner_status;
> end;
> font_name_code: scan_font_ident;
> job_name_code: if job_name=0 then open_log_file;
> end {there are no other cases}
>
> @ @<Print the result of command |c|@>=
> case c of
> number_code: print_int(cur_val);
> roman_numeral_code: print_roman_int(cur_val);
> string_code:if cur_cs<>0 then sprint_cs(cur_cs)
> else print_char(cur_chr);
> meaning_code: print_meaning;
> font_name_code: begin print(font_name[cur_val]);
> if font_size[cur_val]<>font_dsize[cur_val] then
> begin print(" at "); print_scaled(font_size[cur_val]);
> print("pt");
> end;
> end;
> job_name_code: print(job_name);
> end {there are no other cases}
>
7577,7578c9190,9191
< list, and it also makes |cur_val| point to the reference count at the head
< of that list.
---
> list, and it also makes |def_ref| point to the reference count at the
> head of that list.
7584,7585c9197,9198
< \.{\\uppercase}, \.{\\message}, \.{\\errmessage}, \.{\\send}, or
< \.{\\xsend}. In the latter cases a left brace must be scanned next; this
---
> \.{\\uppercase}, \.{\\message}, \.{\\errmessage}, \.{\\write}, or
> \.{\\special}. In the latter cases a left brace must be scanned next; this
7588,7593c9201,9206
< simply be copied from the input using |get_token|. Otherwise all macros
< and occurrences of `\.{\\the}' and `\.{\\minus}' and `\.{\\number}' will
< be expanded, unless they are preceded by some control sequence like
< `\.{\\def}' whose command code is |def|. If both |macro_def| and |xpand|
< are true, the expansion applies only to the macro body (i.e., to the
< material following the first |left_brace| character).
---
> simply be copied from the input using |get_token|. Otherwise all expandable
> tokens will be expanded until unexpandable tokens are left, except that
> the results of expanding `\.{\\the}' are not expanded further.
> If both |macro_def| and |xpand| are true, the expansion applies
> only to the macro body (i.e., to the material following the first
> |left_brace| character).
7595c9208
< The value of |cs_ptr| when |scan_toks| begins should be the |eqtb|
---
> The value of |cur_cs| when |scan_toks| begins should be the |eqtb|
7599,7600d9211
< After the closing right brace, this routine will remove a space. % (rhymes)
<
7603,7604c9214
< var r: pointer; {reference count location}
< @!t:halfword; {token representing the highest parameter number}
---
> var t:halfword; {token representing the highest parameter number}
7610,7611c9220,9223
< begin scanner_status←defining; warning_index←cs_ptr; r←get_avail; info(r)←0;
< p←r; hash_brace←0;
---
> begin if macro_def then scanner_status:=defining
> @+else scanner_status:=absorbing;
> warning_index:=cur_cs; def_ref:=get_avail; token_ref_count(def_ref):=null;
> p:=def_ref; hash_brace:=0; t:=zero_token;
7615,7617c9227,9229
< found: scanner_status←normal; @<Scan an optional space@>;
< if hash_brace≠0 then store_new_token(hash_brace);
< cur_val←r; scan_toks←p;
---
> found: scanner_status:=normal;
> if hash_brace<>0 then store_new_token(hash_brace);
> scan_toks:=p;
7621,7629c9233,9240
< begin t←zero_token; {this represents |"0"|}
< loop@+ begin get_token; {set |cur_cmd|, |cur_chr|, |cur_tok|}
< if cur_cmd≤right_brace then goto done1;
< if cur_cmd=mac_param then
< @<If the next character is a parameter number, make |cur_tok|
< a |match| token; but if it is a left brace, store
< `|left_brace|, |end_match|' and |goto done|@>;
< store_new_token(cur_tok);
< end;
---
> begin loop begin get_token; {set |cur_cmd|, |cur_chr|, |cur_tok|}
> if cur_tok<right_brace_limit then goto done1;
> if cur_cmd=mac_param then
> @<If the next character is a parameter number, make |cur_tok|
> a |match| token; but if it is a left brace, store
> `|left_brace|, |end_match|', set |hash_brace|, and |goto done|@>;
> store_new_token(cur_tok);
> end;
7632c9243
< @<Express shock at the missing left brace; |goto found|@>;
---
> @<Express shock at the missing left brace; |goto found|@>;
7636,7637c9247,9248
< begin print_nl("! Missing { inserted"); incr(align_state);
< @.Missing {\{} inserted@>
---
> begin print_err("Missing { inserted"); incr(align_state);
> @.Missing \{ inserted@>
7639c9250
< ("which I'm going to interpret as `\def\a{}'."); error; goto found;
---
> ("which I'm going to interpret as `\def\a{}'."); error; goto found;
7643c9254
< begin s←match_token+cur_chr; get_token;
---
> begin s:=match_token+cur_chr; get_token;
7645,7647c9256,9259
< begin store_new_token(cur_tok); store_new_token(end_match_token);
< goto done;
< end;
---
> begin hash_brace:=cur_tok;
> store_new_token(cur_tok); store_new_token(end_match_token);
> goto done;
> end;
7649c9261
< begin print_nl("! You already have nine parameters");
---
> begin print_err("You already have nine parameters");
7651,7655c9263,9267
< help1("I'm going to ignore the # sign you just used."); error;
< end
< else begin incr(t);
< if cur_tok≠t then
< begin print_nl("! Parameters must be numbered consecutively");
---
> help1("I'm going to ignore the # sign you just used."); error;
> end
> else begin incr(t);
> if cur_tok<>t then
> begin print_err("Parameters must be numbered consecutively");
7657,7661c9269,9273
< help2("I've inserted the digit you should have used after the #.")@/
< ("Type `1' to delete what you did use."); back_error;
< end;
< cur_tok←s;
< end;
---
> help2("I've inserted the digit you should have used after the #.")@/
> ("Type `1' to delete what you did use."); back_error;
> end;
> cur_tok:=s;
> end;
7665,7685c9277,9303
< unbalance←1;
< loop@+ begin if xpand ∧(cur_cmd≠def) then
< @<Expand the next part of the input@>
< else get_token;
< if cur_cmd≤right_brace then
< if cur_cmd<right_brace then incr(unbalance)
< else begin decr(unbalance);
< if unbalance=0 then goto found;
< end
< else if cur_cmd=mac_param then @<Look for parameter number or \.{\#\#}@>;
< store_new_token(cur_tok);
< end
<
< @ @<Expand the next part of the input@>=
< begin loop begin get_nc_token;
< if cur_cmd=the then q←the_toks(cur_chr>0)
< else if cur_cmd=number then q←num_toks
< else goto done2;
< link(p)←link(temp_head); p←q;
< end;
< done2:
---
> unbalance:=1;
> loop@+ begin if xpand then @<Expand the next part of the input@>
> else get_token;
> if cur_tok<right_brace_limit then
> if cur_cmd<right_brace then incr(unbalance)
> else begin decr(unbalance);
> if unbalance=0 then goto found;
> end
> else if cur_cmd=mac_param then
> if macro_def then @<Look for parameter number or \.{\#\#}@>;
> store_new_token(cur_tok);
> end
>
> @ Here we insert an entire token list created by |the_toks| without
> expanding it further.
>
> @<Expand the next part of the input@>=
> begin loop begin get_next;
> if cur_cmd<=max_command then goto done2;
> if cur_cmd<>the then expand
> else begin q:=the_toks;
> if link(temp_head)<>null then
> begin link(p):=link(temp_head); p:=q;
> end;
> end;
> end;
> done2: x_token
7689,7692c9307,9311
< begin s←cur_tok; get_token;
< if cur_cmd≠mac_param then
< if (cur_chr<"1")∨(cur_chr>t-zero_token+"0")∨@|(cur_cmd≠other_char) then
< begin print_nl("! Illegal parameter number in definition of ");
---
> begin s:=cur_tok;
> if xpand then get_x_token else get_token;
> if cur_cmd<>mac_param then
> if (cur_tok<=zero_token)or(cur_tok>t) then
> begin print_err("Illegal parameter number in definition of ");
7694,7709c9313,9327
< sprint_cs(warning_index);
< help3("You meant to type ## instead of #, right?")@/
< ("Or maybe a } was forgotten somewhere earlier, and things")@/
< ("are all screwed up? I'm going to assume you meant ##.");
< back_error; cur_tok←s;
< end
< else cur_tok←out_param_token-"0"+cur_chr;
< end
< @* \[28] File names.
< Besides the fact that different operating systems treat files in different ways,
< we must cope with the fact that completely different naming conventions
< are used. The following programs show what is required for one particular
< operating system; similar routines for other systems are not difficult
< to devise.
< @^fingers@>
< @^system dependencies@>
---
> sprint_cs(warning_index);
> help3("You meant to type ## instead of #, right?")@/
> ("Or maybe a } was forgotten somewhere earlier, and things")@/
> ("are all screwed up? I'm going to assume that you meant ##.");
> back_error; cur_tok:=s;
> end
> else cur_tok:=out_param_token-"0"+cur_chr;
> end
>
> @ Another way to create a token list is via the \.{\\read} command. The
> sixteen files potentially usable for reading appear in the following
> global variables. The value of |read_open[n]| will be |closed| if
> stream number |n| has not been opened or if it has been fully read;
> |just_open| if an \.{\\openin} but not a \.{\\read} has been done;
> and |normal| if it is open and ready to read the next line.
7711,7723c9329,9330
< \TeX\ assumes that a file name has three parts: the name proper, its
< ``exten\-sion'', and a ``file area'' where it is found in an external file
< system. The extension of an input file or a send file is assumed to be
< `\.{.tex}' unless otherwise specified; it is `\.{.err}' on the error
< transcript file that records each run of \TeX; it is `\.{.tfm}' on the font
< metric files that describe characters in the fonts \TeX\ uses; it is
< `\.{.dvi}' on the output files that specify typesetting information; and it
< is `\.{.fmt}' on the format files written by \.{INITEX} to initialize \TeX.
< The file area can be arbitrary on input files, but it is usually the
< user's current area when a file is output. If an input file cannot be
< found on the specified area, \TeX\ will look for it on a special system
< area; this special area is intended for commonly used input files like
< \.{webhdr.tex}.
---
> @d closed=2 {not open, or at end of file}
> @d just_open=1 {newly opened, first line not yet read}
7725,7734c9332,9334
< Simple uses of \TeX\ refer only to file names that have no explicit extension
< or area. For example, one usually says `\.{\\input paper}' or
< `\.{\\font100=helvetica}' instead of `\.{\\input paper.new}' or
< `\.{\\font100=<csd.knuth>test}'. Simple file names are best, because they
< make the \TeX\ source files portable; whenever a file name consists entirely
< of letters and digits, it should be treated in the same way by all
< implementations of \TeX. However, users need the ability to refer to other
< files in their environment, especially when responding to error messages
< concerning unopenable files; therefore we want to let them use the syntax
< that appears in their favorite operating system.
---
> @<Glob...@>=
> @!read_file:array[0..15] of alpha_file; {used for \.{\\read}}
> @!read_open:array[0..16] of normal..closed; {state of |read_file[n]|}
7736,7748c9336,9337
< @ In order to isolate the system-dependent aspects of file names, the
< @^system dependencies@>
< system-independent parts of \TeX\ make use of three system-dependent
< procedures that are called |begin_name|, |more_name|, and |end_name|. In
< essence, if the user-specified characters of the file name are $c↓1\ldotsm c↓n$,
< the system-independent driver program does the operations
< $$|begin_name|;\,|more_name|(c↓1);\ldotss;|more_name|(c↓n);
< \,|end_name|.$$
< These three procedures communicate with each other via global variables.
< After\-wards the file name will appear in the string pool as three strings
< called |cur_name|\penalty10000\hskip-.05em,
< |cur_area|, and |cur_ext|; the latter two are null (i.e.,
< |""|), unless they were explicitly specified by the user.
---
> @ @<Set init...@>=
> for k:=0 to 16 do read_open[k]:=closed;
7750,7759c9339,9341
< Actually the situation is slightly more complicated, because \TeX\ needs
< to know when the file name ends. The |more_name| routine is a function
< (with side effects) that returns |true| on the calls |more_name|$(c↓1)$,
< $\ldotss$, |more_name|$(c↓{n-1})$. The final call |more_name|$(c↓n)$
< returns |false|; or, it returns |true| and the token following $c↓n$ is
< something like `\.{\\hbox}' (i.e., not a character). In other words,
< |more_name| is supposed to return |true| unless it is sure that the
< file name has been completely scanned; and |end_name| is supposed to be able
< to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
< whether $|more_name|(c↓n)$ returned |true| or |false|.
---
> @ The |read_toks| procedure constructs a token list like that for any
> macro definition, and makes |cur_val| point to it. Parameter |r| points
> to the control sequence that will receive this token list.
7761,7764c9343,9391
< @<Glob...@>=
< @!cur_name:str_number; {name of file just scanned}
< @!cur_area:str_number; {file area just scanned, or \.{""}}
< @!cur_ext:str_number; {file extension just scanned, or \.{""}}
---
> @p procedure read_toks(@!n:integer;@!r:pointer);
> label done;
> var p:pointer; {tail of the token list}
> @!q:pointer; {new node being added to the token list via |store_new_token|}
> @!s:integer; {saved value of |align_state|}
> @!m:small_number; {stream number}
> begin scanner_status:=defining; warning_index:=r;
> def_ref:=get_avail; token_ref_count(def_ref):=null;
> p:=def_ref; {the reference count}
> store_new_token(end_match_token);
> if (n<0)or(n>15) then m:=16@+else m:=n;
> s:=align_state; align_state:=1000000; {disable tab marks, etc.}
> repeat @<Input and store tokens from the next line of the file@>;
> until align_state=1000000;
> cur_val:=def_ref; scanner_status:=normal; align_state:=s;
> end;
>
> @ @<Input and store tokens from the next line of the file@>=
> begin_file_reading; name:=m+1;
> if read_open[m]=closed then @<Input for \.{\\read} from the terminal@>
> else if read_open[m]=just_open then @<Input the first line of |read_file[m]|@>
> else @<Input the next line of |read_file[m]|@>;
> limit:=last;
> if end_line_char_inactive then decr(limit)
> else buffer[limit]:=end_line_char;
> first:=limit+1; loc:=start; state:=new_line;@/
> loop@+ begin get_token;
> if cur_tok=0 then goto done;
> {|cur_cmd=cur_chr=0| will occur at the end of the line}
> if align_state<1000000 then {unmatched `\.\}' aborts the line}
> begin repeat get_token; until cur_tok=0;
> align_state:=1000000; goto done;
> end;
> store_new_token(cur_tok);
> end;
> done: end_file_reading
>
> @ Here we input on-line into the |buffer| array, prompting the user explicitly
> if |n>=0|. The value of |n| is set negative so that additional prompts
> will not be given in the case of multi-line input.
>
> @<Input for \.{\\read} from the terminal@>=
> if interaction>nonstop_mode then
> if n<0 then prompt_input("")
> else begin wake_up_terminal;
> print_ln; sprint_cs(r); prompt_input("="); n:=-1;
> end
> else fatal_error("*** (cannot \read from terminal in nonstop modes)")
> @.cannot \\read@>
7766,7771c9393,9394
< @ The file names we shall deal with have the following structure:
< If the name contains `\.>' or `\.:', the file area consists of all characters
< up to and including the final such character; otherwise the file area is null.
< If the remaining file name contains `\..', the file extension consists of all
< such characters from the first `\..' to the end, otherwise the file extension
< is null.
---
> @ The first line of a file must be treated specially, since |input_ln|
> must be told not to start with |get|.
7774,7775c9397,9416
< We can scan such file names easily by using two global variables that keep track
< of the occurrences of area and extension delimiters:
---
> @<Input the first line of |read_file[m]|@>=
> if input_ln(read_file[m],false) then read_open[m]:=normal
> else begin a_close(read_file[m]); read_open[m]:=closed;
> end
>
> @ An empty line is appended at the end of a |read_file|.
> @^empty line at end of file@>
>
> @<Input the next line of |read_file[m]|@>=
> begin if not input_ln(read_file[m],true) then
> begin a_close(read_file[m]); read_open[m]:=closed;
> if align_state<>1000000 then
> begin runaway;
> print_err("File ended within "); print_esc("read");
> @.File ended within \\read@>
> help1("This \read has unbalanced braces.");
> align_state:=1000000; error;
> end;
> end;
> end
7777,7778c9418,9890
< @<Glob...@>=
< @!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
---
> @* \[28] Conditional processing.
> We consider now the way \TeX\ handles various kinds of \.{\\if} commands.
>
> @d if_char_code=0 { `\.{\\if}' }
> @d if_cat_code=1 { `\.{\\ifcat}' }
> @d if_int_code=2 { `\.{\\ifnum}' }
> @d if_dim_code=3 { `\.{\\ifdim}' }
> @d if_odd_code=4 { `\.{\\ifodd}' }
> @d if_vmode_code=5 { `\.{\\ifvmode}' }
> @d if_hmode_code=6 { `\.{\\ifhmode}' }
> @d if_mmode_code=7 { `\.{\\ifmmode}' }
> @d if_inner_code=8 { `\.{\\ifinner}' }
> @d if_void_code=9 { `\.{\\ifvoid}' }
> @d if_hbox_code=10 { `\.{\\ifhbox}' }
> @d if_vbox_code=11 { `\.{\\ifvbox}' }
> @d ifx_code=12 { `\.{\\ifx}' }
> @d if_eof_code=13 { `\.{\\ifeof}' }
> @d if_true_code=14 { `\.{\\iftrue}' }
> @d if_false_code=15 { `\.{\\iffalse}' }
> @d if_case_code=16 { `\.{\\ifcase}' }
>
> @<Put each...@>=
> primitive("if",if_test,if_char_code);
> @!@:if_char_}{\.{\\if} primitive@>
> primitive("ifcat",if_test,if_cat_code);
> @!@:if_cat_code_}{\.{\\ifcat} primitive@>
> primitive("ifnum",if_test,if_int_code);
> @!@:if_int_}{\.{\\ifnum} primitive@>
> primitive("ifdim",if_test,if_dim_code);
> @!@:if_dim_}{\.{\\ifdim} primitive@>
> primitive("ifodd",if_test,if_odd_code);
> @!@:if_odd_}{\.{\\ifodd} primitive@>
> primitive("ifvmode",if_test,if_vmode_code);
> @!@:if_vmode_}{\.{\\ifvmode} primitive@>
> primitive("ifhmode",if_test,if_hmode_code);
> @!@:if_hmode_}{\.{\\ifhmode} primitive@>
> primitive("ifmmode",if_test,if_mmode_code);
> @!@:if_mmode_}{\.{\\ifmmode} primitive@>
> primitive("ifinner",if_test,if_inner_code);
> @!@:if_inner_}{\.{\\ifinner} primitive@>
> primitive("ifvoid",if_test,if_void_code);
> @!@:if_void_}{\.{\\ifvoid} primitive@>
> primitive("ifhbox",if_test,if_hbox_code);
> @!@:if_hbox_}{\.{\\ifhbox} primitive@>
> primitive("ifvbox",if_test,if_vbox_code);
> @!@:if_vbox_}{\.{\\ifvbox} primitive@>
> primitive("ifx",if_test,ifx_code);
> @!@:ifx_}{\.{\\ifx} primitive@>
> primitive("ifeof",if_test,if_eof_code);
> @!@:if_eof_}{\.{\\ifeof} primitive@>
> primitive("iftrue",if_test,if_true_code);
> @!@:if_true_}{\.{\\iftrue} primitive@>
> primitive("iffalse",if_test,if_false_code);
> @!@:if_false_}{\.{\\iffalse} primitive@>
> primitive("ifcase",if_test,if_case_code);
> @!@:if_case_}{\.{\\ifcase} primitive@>
>
> @ @<Cases of |print_cmd_chr|...@>=
> if_test: case chr_code of
> if_cat_code:print_esc("ifcat");
> if_int_code:print_esc("ifnum");
> if_dim_code:print_esc("ifdim");
> if_odd_code:print_esc("ifodd");
> if_vmode_code:print_esc("ifvmode");
> if_hmode_code:print_esc("ifhmode");
> if_mmode_code:print_esc("ifmmode");
> if_inner_code:print_esc("ifinner");
> if_void_code:print_esc("ifvoid");
> if_hbox_code:print_esc("ifhbox");
> if_vbox_code:print_esc("ifvbox");
> ifx_code:print_esc("ifx");
> if_eof_code:print_esc("ifeof");
> if_true_code:print_esc("iftrue");
> if_false_code:print_esc("iffalse");
> if_case_code:print_esc("ifcase");
> othercases print_esc("if")
> endcases;
>
> @ Conditions can be inside conditions, and this nesting has a stack
> that is independent of the |save_stack|.
>
> Four global variables represent the top of the condition stack:
> |cond_ptr| points to pushed-down entries, if any; |if_limit| specifies
> the largest code of a |fi_or_else| command that is syntactically legal;
> |cur_if| is the name of the current type of conditional; and |if_line|
> is the line number at which it began.
>
> If no conditions are currently in progress, the condition stack has the
> special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|.
> Otherwise |cond_ptr| points to a two-word node; the |type|, |subtype|, and
> |link| fields of the first word contain |if_limit|, |cur_if|, and
> |cond_ptr| at the next level, and the second word contains the
> corresponding |if_line|.
>
> @d if_node_size=2 {number of words in stack entry for conditionals}
> @d if_line_field(#)==mem[#+1].int
> @d if_code=1 {code for \.{\\if...} being evaluated}
> @d fi_code=2 {code for \.{\\fi}}
> @d else_code=3 {code for \.{\\else}}
> @d or_code=4 {code for \.{\\or}}
>
> @<Glob...@>=
> @!cond_ptr:pointer; {top of the condition stack}
> @!if_limit:normal..or_code; {upper bound on |fi_or_else| codes}
> @!cur_if:small_number; {type of conditional being worked on}
> @!if_line:integer; {line where that conditional began}
>
> @ @<Set init...@>=
> cond_ptr:=null; if_limit:=normal; cur_if:=0; if_line:=0;
>
> @ @<Put each...@>=
> primitive("fi",fi_or_else,fi_code);
> @!@:fi_}{\.{\\fi} primitive@>
> text(frozen_fi):="fi"; eqtb[frozen_fi]:=eqtb[cur_val];
> primitive("or",fi_or_else,or_code);
> @!@:or_}{\.{\\or} primitive@>
> primitive("else",fi_or_else,else_code);
> @!@:else_}{\.{\\else} primitive@>
>
> @ @<Cases of |print_cmd_chr|...@>=
> fi_or_else: if chr_code=fi_code then print_esc("fi")
> else if chr_code=or_code then print_esc("or")
> else print_esc("else");
>
> @ When we skip conditional text, we keep track of the line number
> where skipping began, for use in error messages.
>
> @<Glob...@>=
> @!skip_line:integer; {skipping began here}
>
> @ Here is a procedure that ignores text until coming to an \.{\\or},
> \.{\\else}, or \.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$
> nesting. After it has acted, |cur_chr| will indicate the token that
> was found, but |cur_tok| will not be set (because this makes the
> procedure run faster).
>
> @p procedure pass_text;
> label done;
> var l:integer; {level of $\.{\\if}\ldots\.{\\fi}$ nesting}
> @!save_scanner_status:small_number; {|scanner_status| upon entry}
> begin save_scanner_status:=scanner_status; scanner_status:=skipping; l:=0;
> skip_line:=line;
> loop@+ begin get_next;
> if cur_cmd=fi_or_else then
> begin if l=0 then goto done;
> if cur_chr=fi_code then decr(l);
> end
> else if cur_cmd=if_test then incr(l);
> end;
> done: scanner_status:=save_scanner_status;
> end;
>
> @ When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then
> if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if}
> condition has been evaluated, \.{\\relax} will be inserted.
> For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}'
> would otherwise require something after the `\.1'.
>
> @<Push the condition stack@>=
> begin p:=get_node(if_node_size); link(p):=cond_ptr; type(p):=if_limit;
> subtype(p):=cur_if; if_line_field(p):=if_line;
> cond_ptr:=p; cur_if:=cur_chr; if_limit:=if_code; if_line:=line;
> end
>
> @ @<Pop the condition stack@>=
> begin p:=cond_ptr; if_line:=if_line_field(p);
> cur_if:=subtype(p); if_limit:=type(p); cond_ptr:=link(p);
> free_node(p,if_node_size);
> end
>
> @ Here's a procedure that changes the |if_limit| code corresponding to
> a given value of |cond_ptr|.
>
> @p procedure change_if_limit(@!l:small_number;@!p:pointer);
> label exit;
> var q:pointer;
> begin if p=cond_ptr then if_limit:=l {that's the easy case}
> else begin q:=cond_ptr;
> loop@+ begin if q=null then confusion("if");
> @:this can't happen if}{\quad if@>
> if link(q)=p then
> begin type(q):=l; return;
> end;
> q:=link(q);
> end;
> end;
> exit:end;
>
> @ A condition is started when the |expand| procedure encounters
> an |if_test| command; in that case |expand| reduces to |conditional|,
> which is a recursive procedure.
> @^recursion@>
>
> @p procedure conditional;
> label exit,common_ending;
> var b:boolean; {is the condition true?}
> @!r:"<"..">"; {relation to be evaluated}
> @!m,@!n:integer; {to be tested against the second operand}
> @!p,@!q:pointer; {for traversing token lists in \.{\\ifx} tests}
> @!save_scanner_status:small_number; {|scanner_status| upon entry}
> @!save_cond_ptr:pointer; {|cond_ptr| corresponding to this conditional}
> @!this_if:small_number; {type of this conditional}
> begin @<Push the condition stack@>;@+save_cond_ptr:=cond_ptr;this_if:=cur_chr;@/
> @<Either process \.{\\ifcase} or set |b| to the value of a boolean condition@>;
> if tracing_commands>1 then @<Display the value of |b|@>;
> if b then
> begin change_if_limit(else_code,save_cond_ptr);
> return; {wait for \.{\\else} or \.{\\fi}}
> end;
> @<Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|@>;
> common_ending: if cur_chr=fi_code then @<Pop the condition stack@>
> else if_limit:=fi_code; {wait for \.{\\fi}}
> exit:end;
>
> @ In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first
> \.{\\else} that we come to after learning that the \.{\\if} is false is
> not the \.{\\else} we're looking for. Hence the following curious
> logic is needed.
>
> @ @<Skip to \.{\\else} or \.{\\fi}...@>=
> loop@+ begin pass_text;
> if cond_ptr=save_cond_ptr then
> begin if cur_chr<>or_code then goto common_ending;
> print_err("Extra "); print_esc("or");
> @.Extra \\or@>
> help1("I'm ignoring this; it doesn't match any \if.");
> error;
> end
> else if cur_chr=fi_code then @<Pop the condition stack@>;
> end
>
> @ @<Either process \.{\\ifcase} or set |b|...@>=
> case this_if of
> if_char_code, if_cat_code: @<Test if two characters match@>;
> if_int_code, if_dim_code: @<Test relation between integers or dimensions@>;
> if_odd_code: @<Test if an integer is odd@>;
> if_vmode_code: b:=(abs(mode)=vmode);
> if_hmode_code: b:=(abs(mode)=hmode);
> if_mmode_code: b:=(abs(mode)=mmode);
> if_inner_code: b:=(mode<0);
> if_void_code, if_hbox_code, if_vbox_code: @<Test box register status@>;
> ifx_code: @<Test if two tokens match@>;
> if_eof_code: begin scan_four_bit_int; b:=(read_open[cur_val]=closed);
> end;
> if_true_code: b:=true;
> if_false_code: b:=false;
> if_case_code: @<Select the appropriate case
> and |return| or |goto common_ending|@>;
> end {there are no other cases}
>
> @ @<Display the value of |b|@>=
> begin begin_diagnostic;
> if b then print("{true}")@+else print("{false}");
> end_diagnostic(false);
> end
>
> @ Here we use the fact that |"<"|, |"="|, and |">"| are consecutive ASCII
> codes.
> @^ASCII code@>
>
> @<Test relation between integers or dimensions@>=
> begin if this_if=if_int_code then scan_int@+else scan_normal_dimen;
> n:=cur_val; @<Get the next non-blank non-call...@>;
> if (cur_tok>=other_token+"<")and(cur_tok<=other_token+">") then
> r:=cur_tok-other_token
> else begin print_err("Missing = inserted for ");
> @.Missing = inserted@>
> print_cmd_chr(if_test,this_if);
> help1("I was expecting to see `<', `=', or `>'. Didn't.");
> back_error; r:="=";
> end;
> if this_if=if_int_code then scan_int@+else scan_normal_dimen;
> case r of
> "<": b:=(n<cur_val);
> "=": b:=(n=cur_val);
> ">": b:=(n>cur_val);
> end;
> end
>
> @ @<Test if an integer is odd@>=
> begin scan_int; b:=odd(cur_val);
> end
>
> @ @<Test box register status@>=
> begin scan_eight_bit_int; p:=box(cur_val);
> if this_if=if_void_code then b:=(p=null)
> else if p=null then b:=false
> else if this_if=if_hbox_code then b:=(type(p)=hlist_node)
> else b:=(type(p)=vlist_node);
> end
>
> @ An active character will be treated as category 13 following
> \.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}. We use the fact that
> active characters have the smallest tokens, among all control sequences.
>
> @d get_x_token_or_active_char==@t@>@;
> begin get_x_token;
> if cur_cmd=relax then if cur_chr=no_expand_flag then
> begin cur_cmd:=active_char;
> cur_chr:=cur_tok-cs_token_flag-active_base;
> end;
> end
>
> @<Test if two characters match@>=
> begin get_x_token_or_active_char;
> if (cur_cmd>active_char)or(cur_chr>255) then {not a character}
> begin m:=relax; n:=256;
> end
> else begin m:=cur_cmd; n:=cur_chr;
> end;
> get_x_token_or_active_char;
> if (cur_cmd>active_char)or(cur_chr>255) then
> begin cur_cmd:=relax; cur_chr:=256;
> end;
> if this_if=if_char_code then b:=(n=cur_chr)@+else b:=(m=cur_cmd);
> end
>
> @ Note that `\.{\\ifx}' will declare two macros different if one is \\{long}
> or \\{outer} and the other isn't, even though the texts of the macros are
> the same.
>
> We need to reset |scanner_status|, since \.{\\outer} control sequences
> are allowed, but we might be scanning a macro definition or preamble.
>
> @<Test if two tokens match@>=
> begin save_scanner_status:=scanner_status; scanner_status:=normal;
> get_next; n:=cur_cs; p:=cur_cmd; q:=cur_chr;
> get_next; if cur_cmd<>p then b:=false
> else if cur_cmd<call then b:=(cur_chr=q)
> else @<Test if two macro texts match@>;
> scanner_status:=save_scanner_status;
> end
>
> @ Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are
> different in examples like this:
> $$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr
> {}\\def\\a\{\\c\}&
> {}\\def\\c\{\}\cr
> {}\\def\\b\{\\d\}&
> {}\\def\\d\{\}\cr}}$$
>
> @<Test if two macro texts match@>=
> begin p:=link(cur_chr); q:=link(equiv(n)); {omit reference counts}
> if p=q then b:=true
> else begin while (p<>null)and(q<>null) do
> if info(p)<>info(q) then p:=null
> else begin p:=link(p); q:=link(q);
> end;
> b:=((p=null)and(q=null));
> end;
> end
>
> @ @<Select the appropriate case and |return| or |goto common_ending|@>=
> begin scan_int; n:=cur_val; {|n| is the number of cases to pass}
> if tracing_commands>1 then
> begin begin_diagnostic; print("{case "); print_int(n); print_char("}");
> end_diagnostic(false);
> end;
> while n<>0 do
> begin pass_text;
> if cond_ptr=save_cond_ptr then
> if cur_chr=or_code then decr(n)
> else goto common_ending
> else if cur_chr=fi_code then @<Pop the condition stack@>;
> end;
> change_if_limit(or_code,save_cond_ptr);
> return; {wait for \.{\\or}, \.{\\else}, or \.{\\fi}}
> end
>
> @ The processing of conditionals is complete except for the following
> code, which is actually part of |expand|. It comes into play when
> \.{\\or}, \.{\\else}, or \.{\\fi} is scanned.
>
> @<Terminate the current conditional and skip to \.{\\fi}@>=
> if cur_chr>if_limit then
> if if_limit=if_code then insert_relax {condition not yet evaluated}
> else begin print_err("Extra "); print_cmd_chr(fi_or_else,cur_chr);
> @.Extra \\or@>
> @.Extra \\else@>
> @.Extra \\fi@>
> help1("I'm ignoring this; it doesn't match any \if.");
> error;
> end
> else begin while cur_chr<>fi_code do pass_text; {skip to \.{\\fi}}
> @<Pop the condition stack@>;
> end
>
> @* \[29] File names.
> It's time now to fret about file names. Besides the fact that different
> operating systems treat files in different ways, we must cope with the
> fact that completely different naming conventions are used by different
> groups of people. The following programs show what is required for one
> particular operating system; similar routines for other systems are not
> difficult to devise.
> @^fingers@>
> @^system dependencies@>
>
> \TeX\ assumes that a file name has three parts: the name proper; its
> ``extension''; and a ``file area'' where it is found in an external file
> system. The extension of an input file or a write file is assumed to be
> `\.{.tex}' unless otherwise specified; it is `\.{.log}' on the
> transcript file that records each run of \TeX; it is `\.{.tfm}' on the font
> metric files that describe characters in the fonts \TeX\ uses; it is
> `\.{.dvi}' on the output files that specify typesetting information; and it
> is `\.{.fmt}' on the format files written by \.{INITEX} to initialize \TeX.
> The file area can be arbitrary on input files, but files are usually
> output to the user's current area. If an input file cannot be
> found on the specified area, \TeX\ will look for it on a special system
> area; this special area is intended for commonly used input files like
> \.{webmac.tex}.
>
> Simple uses of \TeX\ refer only to file names that have no explicit
> extension or area. For example, a person usually says `\.{\\input} \.{paper}'
> or `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input}
> \.{paper.new}' or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file
> names are best, because they make the \TeX\ source files portable;
> whenever a file name consists entirely of letters and digits, it should be
> treated in the same way by all implementations of \TeX. However, users
> need the ability to refer to other files in their environment, especially
> when responding to error messages concerning unopenable files; therefore
> we want to let them use the syntax that appears in their favorite
> operating system.
>
> The following procedures don't allow spaces to be part of
> file names; but some users seem to like names that are spaced-out.
> System-dependent changes to allow such things should probably
> be made with reluctance, and only when an entire file name that
> includes spaces is ``quoted'' somehow.
>
> @ In order to isolate the system-dependent aspects of file names, the
> @^system dependencies@>
> system-independent parts of \TeX\ are expressed in terms
> of three system-dependent
> procedures called |begin_name|, |more_name|, and |end_name|. In
> essence, if the user-specified characters of the file name are $c_1\ldots c_n$,
> the system-independent driver program does the operations
> $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n);
> \,|end_name|.$$
> These three procedures communicate with each other via global variables.
> Afterwards the file name will appear in the string pool as three strings
> called |cur_name|\penalty10000\hskip-.05em,
> |cur_area|, and |cur_ext|; the latter two are null (i.e.,
> |""|), unless they were explicitly specified by the user.
>
> Actually the situation is slightly more complicated, because \TeX\ needs
> to know when the file name ends. The |more_name| routine is a function
> (with side effects) that returns |true| on the calls |more_name|$(c_1)$,
> \dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$
> returns |false|; or, it returns |true| and the token following $c_n$ is
> something like `\.{\\hbox}' (i.e., not a character). In other words,
> |more_name| is supposed to return |true| unless it is sure that the
> file name has been completely scanned; and |end_name| is supposed to be able
> to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
> whether $|more_name|(c_n)$ returned |true| or |false|.
>
> @<Glob...@>=
> @!cur_name:str_number; {name of file just scanned}
> @!cur_area:str_number; {file area just scanned, or \.{""}}
> @!cur_ext:str_number; {file extension just scanned, or \.{""}}
>
> @ The file names we shall deal with for illustrative purposes have the
> following structure: If the name contains `\.>' or `\.:', the file area
> consists of all characters up to and including the final such character;
> otherwise the file area is null. If the remaining file name contains
> `\..', the file extension consists of all such characters from the first
> remaining `\..' to the end, otherwise the file extension is null.
> @^system dependencies@>
>
> We can scan such file names easily by using two global variables that keep track
> of the occurrences of area and extension delimiters:
>
> @<Glob...@>=
> @!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
7782,7784c9894,9897
< system area called |TEX_area|. Font files whose areas are not given explicitly
< are assumed to appear in a standard system area called |TEX_font_area|.
< These system area names will, of course, vary from place to place.
---
> system area called |TEX_area|. Font metric files whose areas are not given
> explicitly are assumed to appear in a standard system area called
> |TEX_font_area|. These system area names will, of course, vary from place
> to place.
7787,7788c9900,9903
< @d TEX_area=="<TeX>"
< @d TEX_font_area=="<TeX.fonts>"
---
> @d TEX_area=="TeXinputs:"
> @.TeXinputs@>
> @d TEX_font_area=="TeXfonts:"
> @.TeXfonts@>
7794c9909
< begin area_delimiter←0; ext_delimiter←0;
---
> begin area_delimiter:=0; ext_delimiter:=0;
7797c9912,9915
< @ And here's the second.
---
> @ And here's the second. The string pool might change as the file name is
> being scanned, since a new \.{\\csname} might be entered; therefore we keep
> |area_delimiter| and |ext_delimiter| relative to the beginning of the current
> string, instead of assigning an absolute address like |pool_ptr| to them.
7800,7808c9918,9926
< @p function more_name(@!c:ascii_code):boolean;
< begin if c=" " then more_name←false
< else begin if (c=">")∨(c=":") then
< begin area_delimiter←pool_ptr; ext_delimiter←0;
< end
< else if (c=".")∧(ext_delimiter=0) then ext_delimiter←pool_ptr;
< str_room(1); append_char(c); {contribute |c| to the current string}
< more_name←true;
< end;
---
> @p function more_name(@!c:ASCII_code):boolean;
> begin if c=" " then more_name:=false
> else begin str_room(1); append_char(c); {contribute |c| to the current string}
> if (c=">")or(c=":") then
> begin area_delimiter:=cur_length; ext_delimiter:=0;
> end
> else if (c=".")and(ext_delimiter=0) then ext_delimiter:=cur_length;
> more_name:=true;
> end;
7815,7819c9933,9939
< begin if str_ptr+3>max_strings then overflow("number of strings",max_strings);
< if area_delimiter=0 then cur_area←""
< else begin cur_area←str_ptr; incr(str_ptr);
< str_start[str_ptr]←area_delimiter+1;
< end;
---
> begin if str_ptr+3>max_strings then
> overflow("number of strings",max_strings-init_str_ptr);
> @:TeX capacity exceeded number of strings}{\quad number of strings@>
> if area_delimiter=0 then cur_area:=""
> else begin cur_area:=str_ptr;
> str_start[str_ptr+1]:=str_start[str_ptr]+area_delimiter; incr(str_ptr);
> end;
7821,7825c9941,9946
< begin cur_ext←""; cur_name←make_string;
< end
< else begin cur_name←str_ptr; incr(str_ptr);
< str_start[str_ptr]←ext_delimiter; cur_ext←make_string;
< end;
---
> begin cur_ext:=""; cur_name:=make_string;
> end
> else begin cur_name:=str_ptr;
> str_start[str_ptr+1]:=str_start[str_ptr]+ext_delimiter-area_delimiter-1;
> incr(str_ptr); cur_ext:=make_string;
> end;
7835c9956
< begin print(a); print(n); print(e);
---
> begin slow_print(a); slow_print(n); slow_print(e);
7838,7839c9959,9962
< @ Another system-dependent routine is needed to convert three \TeX\ strings
< into the |name_of_file| value that is used to open files.
---
> @ Another system-dependent routine is needed to convert three internal
> \TeX\ strings
> into the |name_of_file| value that is used to open files. The present code
> allows both lowercase and uppercase letters in the file name.
7842,7845c9965,9967
< @d append_to_name(#)==begin c←#; incr(k);
< if (c≥"a")∧(c≤"z") then c←c-@'40; {convert to upper case}
< name_of_file[k]←xchr[c];
< end
---
> @d append_to_name(#)==begin c:=#; incr(k);
> if k<=file_name_size then name_of_file[k]:=xchr[c];
> end
7849,7858c9971,9978
< @!c: ascii_code; {character being packed}
< @!j:pool_pointer; {index into |string_pool|}
< begin if length(a)+length(n)+length(e)>file_name_size then
< overflow("file name size",file_name_size);
< k←0;
< for j←str_start[a] to str_start[a+1]-1 do append_to_name(str_pool[j]);
< for j←str_start[n] to str_start[n+1]-1 do append_to_name(str_pool[j]);
< for j←str_start[e] to str_start[e+1]-1 do append_to_name(str_pool[j]);
< name_length←k;
< for k←name_length+1 to file_name_size do name_of_file[k]←' ';
---
> @!c: ASCII_code; {character being packed}
> @!j:pool_pointer; {index into |str_pool|}
> begin k:=0;
> for j:=str_start[a] to str_start[a+1]-1 do append_to_name(so(str_pool[j]));
> for j:=str_start[n] to str_start[n+1]-1 do append_to_name(so(str_pool[j]));
> for j:=str_start[e] to str_start[e+1]-1 do append_to_name(so(str_pool[j]));
> if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
> for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
7867,7868c9987,9990
< @d format_default_length=22 {length of the |TEX_format_default| string}
< @d format_area_length=13 {length of its area part}
---
> @d format_default_length=20 {length of the |TEX_format_default| string}
> @d format_area_length=11 {length of its area part}
> @d format_ext_length=4 {length of its `\.{.fmt}' part}
> @d format_extension=".fmt" {the extension, as a \.{WEB} constant}
7874c9996,9999
< TEX_format_default←'<TeX.formats>BASIC.fmt';
---
> TEX_format_default:='TeXformats:plain.fmt';
> @.TeXformats@>
> @.plain@>
> @^system dependencies@>
7877c10002
< if format_default_length>file_name_size then bad←31;
---
> if format_default_length>file_name_size then bad:=31;
7881c10006,10007
< |buffer[a..b]|, followed by the last four characters of |format_default|.
---
> |buffer[a..b]|, followed by the last |format_ext_length| characters of
> |TEX_format_default|.
7891c10017
< @!c: ascii_code; {character being packed}
---
> @!c: ASCII_code; {character being packed}
7893,7900c10019,10027
< begin if n+b-a+5>file_name_size then b←a+file_name_size-n-5;
< k←0;
< for j←1 to n do append_to_name(xord[TEX_format_default[j]]);
< for j←a to b do append_to_name(buffer[j]);
< for j←format_default_length-3 to format_default_length do
< append_to_name(xord[TEX_format_default[j]]);
< name_length←k;
< for k←name_length+1 to file_name_size do name_of_file[k]←' ';
---
> begin if n+b-a+1+format_ext_length>file_name_size then
> b:=a+file_name_size-n-1-format_ext_length;
> k:=0;
> for j:=1 to n do append_to_name(xord[TEX_format_default[j]]);
> for j:=a to b do append_to_name(buffer[j]);
> for j:=format_default_length-format_ext_length+1 to format_default_length do
> append_to_name(xord[TEX_format_default[j]]);
> if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
> for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
7905,7906c10032,10035
< the preliminary initialization. The buffer contains the first line of input
< in |buffer[loc..(last-1)]|, where |loc<last| and |buffer[loc]≠" "|.
---
> the preliminary initialization, or when the user is substituting another
> format file by typing `\.\&' after the initial `\.{**}' prompt. The buffer
> contains the first line of input in |buffer[loc..(last-1)]|, where
> |loc<last| and |buffer[loc]<>" "|.
7911,7931c10040,10064
< var j:0..buf_size; {the first space after the file name}
< begin if buffer[loc]≠"\" then
< begin j←loc+1; buffer[last]←" ";
< while buffer[j]≠" " do incr(j);
< pack_buffered_name(0,loc,j-1); {try first without the system file area}
< if w_open_in(fmt_file) then
< begin loc←j; goto found;
< end;@/
< {now try the system format file area}
< pack_buffered_name(format_area_length,loc,j-1);
< if w_open_in(fmt_file) then
< begin loc←j; goto found;
< end;
< end;
< {now pull out all the stops: try for the system \.{BASIC} file}
< pack_buffered_name(format_default_length-4,1,0);
< if ¬ w_open_in(fmt_file) then
< begin write_ln(term_out,'I can''t find the BASIC format file!');
< open_fmt_file←false; return;
< end;
< found:open_fmt_file←true;
---
> var j:0..buf_size; {the first space after the format file name}
> begin j:=loc;
> if buffer[loc]="&" then
> begin incr(loc); j:=loc; buffer[last]:=" ";
> while buffer[j]<>" " do incr(j);
> pack_buffered_name(0,loc,j-1); {try first without the system file area}
> if w_open_in(fmt_file) then goto found;
> pack_buffered_name(format_area_length,loc,j-1);
> {now try the system format file area}
> if w_open_in(fmt_file) then goto found;
> wake_up_terminal;
> wterm_ln('Sorry, I can''t find that format;',' will try PLAIN.');
> @.Sorry, I can't find...@>
> update_terminal;
> end;
> {now pull out all the stops: try for the system \.{plain} file}
> pack_buffered_name(format_default_length-format_ext_length,1,0);
> if not w_open_in(fmt_file) then
> begin wake_up_terminal;
> wterm_ln('I can''t find the PLAIN format file!');
> @.I can't find PLAIN...@>
> @.plain@>
> open_fmt_file:=false; return;
> end;
> found:loc:=j; open_fmt_file:=true;
7937,7938c10070,10071
< ideally be changed to deduce the full name of file@@|f|, if it is
< possible to do this in a \PASCAL\ program.
---
> ideally be changed to deduce the full name of file~|f|, which is the file
> most recently opened, if it is possible to do this in a \PASCAL\ program.
7940a10074,10076
> This routine might be called after string memory has overflowed, hence
> we dare not use `|str_room|'.
>
7943,7945c10079,10084
< begin str_room(name_length);
< for k←1 to name_length do append_char(xord[name_of_file[k]]);
< make_name_string←make_string;
---
> begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings)or
> (cur_length>0) then
> make_name_string:="?"
> else begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
> make_name_string:=make_string;
> end;
7948c10087
< begin a_make_name_string←make_name_string;
---
> begin a_make_name_string:=make_name_string;
7951c10090
< begin b_make_name_string←make_name_string;
---
> begin b_make_name_string:=make_name_string;
7954,7964c10093
< begin w_make_name_string←make_name_string;
< end;
<
< @ Similarly, the ideal |write_name_string| would write the full
< external name of |fmt_file|, assuming that |fmt_file| has been opened.
< The procedure shown here merely copies |name_of_file|. Output in this
< case goes to the user's terminal.
<
< @p procedure write_name_string;
< var k:1..file_name_size; {index into |name_of_file|}
< begin for k←1 to name_length do write(term_out,name_of_file[k]);
---
> begin w_make_name_string:=make_name_string;
7967,7970c10096,10099
< @ Now let's consider the routines by which \TeX\ deals with file names
< in a (hopefully) system-independent manner.
< First comes a procedure that looks for a file name in the input by
< calling |get_nc_token| for the information.
---
> @ Now let's consider the ``driver''
> routines by which \TeX\ deals with file names
> in a system-independent manner. First comes a procedure that looks for a
> file name in the input by calling |get_x_token| for the information.
7974c10103
< begin begin_name;
---
> begin name_in_progress:=true; begin_name;
7976,7982c10105,10111
< loop@+begin if (cur_cmd>other_char)∨(cur_chr>127) then {not a character}
< begin back_input; goto done;
< end;
< if ¬ more_name(cur_chr) then goto done;
< get_nc_token;
< end;
< done: end_name;
---
> loop@+begin if (cur_cmd>other_char)or(cur_chr>255) then {not a character}
> begin back_input; goto done;
> end;
> if not more_name(cur_chr) then goto done;
> get_x_token;
> end;
> done: end_name; name_in_progress:=false;
7985,7987c10114,10124
< @ The global variable |job_name| contains the file name that was first
< \.{\\input} by the user. This name is extended by `\.{err}' and `\.{dvi}'
< and `\.{fmt}' in order to make the names of \TeX's output files.
---
> @ The global variable |name_in_progress| is used to prevent recursive
> use of |scan_file_name|, since the |begin_name| and other procedures
> communicate via global variables. Recursion would arise only by
> devious tricks like `\.{\\input\\input f}'; such attempts at sabotage
> must be thwarted. Furthermore, |name_in_progress| prevents \.{\\input}
> @^recursion@>
> from being initiated when a font size specification is being scanned.
>
> Another global variable, |job_name|, contains the file name that was first
> \.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}'
> and `\.{.fmt}' in the names of \TeX's output files.
7989a10127
> @!name_in_progress:boolean; {is a file name being scanned?}
7990a10129
> @!log_opened:boolean; {has the transcript file been opened?}
7993c10132,10133
< We have |job_name=0| if and only the `\.{err}' file has not been opened.
---
> We have |job_name=0| if and only if the `\.{log}' file has not been opened,
> except of course for a short time just after |job_name| has become nonzero.
7995c10135,10136
< @<Initialize the output...@>=job_name←0;
---
> @<Initialize the output...@>=
> job_name:=0; name_in_progress:=false; log_opened:=false;
7998c10139
< |job_name≠0|. It ignores and changes the current settings of |cur_area|
---
> |job_name<>0|. It ignores and changes the current settings of |cur_area|
8003,8006c10144,10147
< @p procedure pack_job_name(@!s:str_number); {|s = ".err"|, |".dvi"|, or
< |".fmt"|}
< begin cur_area←""; cur_ext←s;
< cur_name←job_name; pack_cur_name;
---
> @p procedure pack_job_name(@!s:str_number); {|s = ".log"|, |".dvi"|, or
> |format_extension|}
> begin cur_area:=""; cur_ext:=s;
> cur_name:=job_name; pack_cur_name;
8010,8011c10151,10152
< routine calls upon the user to supply another file name. Parameter@@|s|
< is used in the error message to identify the type of file; parameter@@|e|
---
> routine calls upon the user to supply another file name. Parameter~|s|
> is used in the error message to identify the type of file; parameter~|e|
8019c10160,10161
< begin if s="input file name" then print_nl("! I can't find file `")
---
> begin if interaction=scroll_mode then wake_up_terminal;
> if s="input file name" then print_err("I can't find file `")
8021c10163
< else print_nl("! I can't write on file `");
---
> else print_err("I can't write on file `");
8025a10168
> @.Please type...@>
8027c10170,10171
< fatal_error("*** (job aborted, file error in nonstop mode)");
---
> fatal_error("*** (job aborted, file error in nonstop mode)");
> @.job aborted, file error...@>
8029c10173
< if cur_ext="" then cur_ext←e;
---
> if cur_ext="" then cur_ext:=e;
8034,8039c10178,10183
< begin begin_name; k←first;
< while (buffer[k]=" ")∧(k<last) do incr(k);
< loop@+ begin if k=last then goto done;
< if ¬ more_name(buffer[k]) then goto done;
< incr(k);
< end;
---
> begin begin_name; k:=first;
> while (buffer[k]=" ")and(k<last) do incr(k);
> loop@+ begin if k=last then goto done;
> if not more_name(buffer[k]) then goto done;
> incr(k);
> end;
8043,8044c10187,10188
< @ Here's an example of how these conventions are used. We shall use the
< macro |ensure_dvi_open| when it is time to ship out a box of stuff.
---
> @ Here's an example of how these conventions are used. Whenever it is time to
> ship out a box of stuff, we shall use the macro |ensure_dvi_open|.
8047,8052c10191,10196
< begin if job_name=0 then open_err_file;
< pack_job_name(".dvi");
< while ¬ b_open_out(dvi_file) do
< prompt_file_name("file name for output",".dvi");
< output_file_name←b_make_name_string(dvi_file);
< end
---
> begin if job_name=0 then open_log_file;
> pack_job_name(".dvi");
> while not b_open_out(dvi_file) do
> prompt_file_name("file name for output",".dvi");
> output_file_name:=b_make_name_string(dvi_file);
> end
8056a10201
> @!log_name:str_number; {full name of the log file}
8058c10203
< @ @<Initialize the output...@>=output_file_name←0;
---
> @ @<Initialize the output...@>=output_file_name:=0;
8060c10205
< @ The |open_err_file| routine is used to open the transcript file and to help
---
> @ The |open_log_file| routine is used to open the transcript file and to help
8063c10208
< @p procedure open_err_file;
---
> @p procedure open_log_file;
8065a10211
> @!l:0..buf_size; {end of first input line}
8067,8072c10213,10219
< begin old_setting←selector; print_nl("");
< if job_name=0 then job_name←"texput";
< pack_job_name(".err");
< while ¬ a_open_out(err_file) do
< prompt_file_name("`err' file name for the transcript",".err");
< selector←err_only;
---
> begin old_setting:=selector;
> if job_name=0 then job_name:="texput";
> @.texput@>
> pack_job_name(".log");
> while not a_open_out(log_file) do @<Try to get a different log file name@>;
> log_name:=a_make_name_string(log_file);
> selector:=log_only; log_opened:=true;
8074,8075c10221,10226
< input_stack[input_ptr]←cur_input; {make sure bottom level is in memory}
< for k←1 to input_stack[0].limit_field do print(buffer[k-1]);
---
> input_stack[input_ptr]:=cur_input; {make sure bottom level is in memory}
> print_nl("**");
> @.**@>
> l:=input_stack[0].limit_field; {last position of first line}
> if buffer[l]=end_line_char then decr(l);
> for k:=1 to l do print(buffer[k]);
8077c10228
< selector←old_setting+2;
---
> selector:=old_setting+2; {|log_only| or |term_and_log|}
8079a10231,10249
> @ Sometimes |open_log_file| is called at awkward moments when \TeX\ is
> unable to print error messages or even to |show_context|.
> The |prompt_file_name| routine can result in a |fatal_error|, but the |error|
> routine will not be invoked because |log_opened| will be false.
>
> The normal idea of |batch_mode| is that nothing at all should be written
> on the terminal. However, in the unusual case that
> no log file could be opened, we make an exception and allow
> an explanatory message to be seen.
>
> Incidentally, the program always refers to the log file as a `\.{transcript
> file}', because some systems cannot use the extension `\.{.log}' for
> this file.
>
> @<Try to get a different log file name@>=
> begin selector:=term_only;
> prompt_file_name("transcript file name",".log");
> end
>
8081,8082c10251,10252
< begin write(err_file,banner);
< print(format_ident); print(" ");
---
> begin wlog(banner);
> slow_print(format_ident); print(" ");
8084,8085c10254,10255
< months←'JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC';
< for k←3*month-2 to 3*month do write(err_file,months[k]);
---
> months:='JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC';
> for k:=3*month-2 to 3*month do wlog(months[k]);
8087,8089c10257
< print_int(time div 60); print_char(":");
< print_char("0"+((time mod 60) div 10));
< print_char("0"+(time mod 10)); print_ln;
---
> print_two(time div 60); print_char(":"); print_two(time mod 60);
8098c10266
< if cur_ext="" then cur_ext←".tex";
---
> if cur_ext="" then cur_ext:=".tex";
8100,8107c10268,10277
< loop@+ begin begin_file_reading; {set up |cur_file| and new level of input}
< if a_open_in(cur_file) then goto done;
< pack_file_name(cur_name,TEX_area,cur_ext);
< if a_open_in(cur_file) then goto done;
< end_file_reading; {remove the level that didn't work}
< prompt_file_name("input file name",".tex");
< end;
< done: name←a_make_name_string(cur_file); state←new_line;
---
> loop@+ begin begin_file_reading; {set up |cur_file| and new level of input}
> if a_open_in(cur_file) then goto done;
> if cur_area="" then
> begin pack_file_name(cur_name,TEX_area,cur_ext);
> if a_open_in(cur_file) then goto done;
> end;
> end_file_reading; {remove the level that didn't work}
> prompt_file_name("input file name",".tex");
> end;
> done: name:=a_make_name_string(cur_file);
8109,8112c10279,10288
< begin job_name←cur_name; open_err_file;
< end; {|open_err_file| doesn't |show_context|, so |limit|
< and |loc| needn't be set to meaningful values yet}
< print(" ("); print(name); update_terminal;
---
> begin job_name:=cur_name; open_log_file;
> end; {|open_log_file| doesn't |show_context|, so |limit|
> and |loc| needn't be set to meaningful values yet}
> if term_offset+length(name)>max_print_line-2 then print_ln
> else if (term_offset>0)or(file_offset>0) then print_char(" ");
> print_char("("); incr(open_parens); slow_print(name); update_terminal;
> state:=new_line;
> if name=str_ptr-1 then {we can conserve string pool space now}
> begin flush_string; name:=cur_name;
> end;
8116,8118c10292,10296
< @ Here we have to remember that the |input_ln| routine
< starts with a |get|, so we have to read the first character of the file
< before it is lost.
---
> @ Here we have to remember to tell the |input_ln| routine not to
> start with a |get|. If the file is empty, it is considered to
> contain a single blank line.
> @^system dependencies@>
> @^empty line at end of file@>
8121,8125c10299,10300
< begin if eoln(cur_file) then last←start
< else begin buffer[start]←xord[cur_file^]; first←start+1;
< if ¬ input_ln(cur_file) then confusion("input");
< @:confusion input}{\quad input@>
< end;
---
> begin line:=1;
> if input_ln(cur_file,false) then do_nothing;
8127c10302,10304
< buffer[limit]←carriage_return; first←limit+1; loc←start; line←1;
---
> if end_line_char_inactive then decr(limit)
> else buffer[limit]:=end_line_char;
> first:=limit+1; loc:=start;
8129c10306,10307
< @* \[29] Font metric data.
---
>
> @* \[30] Font metric data.
8131c10309
< \.{TFM} files; the `\.T' in `\.{TFM} stands for \TeX,
---
> \.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX,
8163c10341
< They are all nonnegative and less than $2^{15}$. We must have |bc-1≤ec≤255|,
---
> They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
8167,8170c10345
< and as few as 0 characters (if |bc=ec+1|). An exception to these rules is
< planned for oriental fonts, which will be identified by the condition |ec=256|;
< such fonts are not allowed except in extensions to \TeX82.
< @^oriental characters@>@^Chinese characters@>@^Japanese characters@>
---
> and as few as 0 characters (if |bc=ec+1|).
8206,8207c10381,10382
< scheme used (e.g., `\.{XEROX TEXT}' or `\.{TeX MATHSY}'), the next five
< give the font family name (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
---
> scheme used (e.g., `\.{XEROX text}' or `\.{TeX math symbols}'), the next five
> give the font identifier (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
8211c10386
< is not explicitly repeated in \.{DVI} format.
---
> is not explicitly repeated in \.{DVI}~format.
8226c10401
< the font, in units of points. This number must be at least 1.0; it is
---
> the font, in units of \TeX\ points. This number must be at least 1.0; it is
8231c10406
< and replace it by $\delta$, and to multiply the $x$ and@@$y$ coordinates
---
> and replace it by $\delta$, and to multiply the $x$ and~$y$ coordinates
8233,8235c10408,10411
< design size. {\sl All other dimensions in the\/\ \.{TFM} file are
< |fix_word| numbers in design-size units.} Thus, for example, the value
< of |param[6]|, one \.{em} or \.{\\quad}, is often the |fix_word| value
---
> design size. {\sl All other dimensions in the\/ \.{TFM} file are
> |fix_word|\kern-1pt\ numbers in design-size units}, with the exception of
> |param[1]| (which denotes the slant ratio). Thus, for example, the value
> of |param[6]|, which defines the \.{em} unit, is often the |fix_word| value
8242,8244c10418,10420
< @ Next comes the |char_info| array, which contains one |char_info_word|
< per character. Each |@!char_info_word| contains six fields packed into
< four bytes as follows.
---
> @ Next comes the |char_info| array, which contains one |@!char_info_word|
> per character. Each word in this part of the file contains six fields
> packed into four bytes as follows.
8248c10424
< (4@@bits)\par
---
> (4~bits)\par
8250c10426
< (2@@bits)\par
---
> (2~bits)\par
8261,8262c10437,10438
< The italic correction of a character has three different uses.
< (a)@@In ordinary text, the italic correction is added to the width only if
---
> The italic correction of a character has two different uses.
> (a)~In ordinary text, the italic correction is added to the width only if
8264,8269c10440,10441
< (b)@@In math formulas, the italic correction is always added to the width,
< unless the character has a subscript but no superscript.
< (c)@@In math formulas for which the character is a mathop in display style,
< a nonzero italic correction also affects the ``limit switch.'' For example,
< an integral sign $\int$ has a nonzero italic correction, but a summation
< sign $\sum$ does not.
---
> (b)~In math formulas, the italic correction is always added to the width,
> except with respect to the positioning of subscripts.
8280,8283c10452,10455
< \yskip\hang|tag=0| (|no_tag|) means that |remainder| is unused.\par
< \hang|tag=1| (|lig_tag|) means that this character has a ligature/kerning
< program starting at |lig_kern[remainder]|.\par
< \hang|tag=2| (|list_tag|) means that this character is part of a chain of
---
> \yskip\hangg|tag=0| (|no_tag|) means that |remainder| is unused.\par
> \hangg|tag=1| (|lig_tag|) means that this character has a ligature/kerning
> program starting at position |remainder| in the |lig_kern| array.\par
> \hangg|tag=2| (|list_tag|) means that this character is part of a chain of
8286c10458
< \hang|tag=3| (|ext_tag|) means that this character code represents an
---
> \hangg|tag=3| (|ext_tag|) means that this character code represents an
8305,8306c10477,10479
< \yskip\hang first byte: |stop_bit|, indicates the final program step
< if the byte is 128 or more.\par
---
> \yskip\hang first byte: |skip_byte|, indicates that this is the final program
> step if the byte is 128 or more, otherwise the next step is obtained by
> skipping this number of intervening steps.\par
8308,8310c10481,10483
< then perform the operation and stop, otherwise continue.''\par
< \hang third byte: |op_bit|, indicates a ligature step if less than@@128,
< a kern step otherwise.\par
---
> then perform the operation and stop, otherwise continue.''\par
> \hang third byte: |op_byte|, indicates a ligature step if less than~128,
> a kern step otherwise.\par
8313,8316c10486,10488
< In a ligature step the current character and |next_char| are replaced by
< the single character whose code is |remainder|. In a kern step, an
< additional space equal to |@!kern[remainder]| is inserted between the
< current character and |next_char|. (The value of |kern[remainder]| is
---
> In a kern step, an
> additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
> between the current character and |next_char|. This amount is
8318c10490
< by kerning, but it might be positive.)
---
> by kerning; but it might be positive.
8320,8323c10492,10525
< @d stop_flag=128+min_quarterword
< {value indicating `\.{STOP}' in a lig/kern program}
< @d kern_flag=128+min_quarterword {op code for a kern step}
< @d stop_bit(#)==#.b0
---
> There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
> $0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
> |remainder| is inserted between the current character and |next_char|;
> then the current character is deleted if $b=0$, and |next_char| is
> deleted if $c=0$; then we pass over $a$~characters to reach the next
> current character (which may have a ligature/kerning program of its own).
>
> If the very first instruction of the |lig_kern| array has |skip_byte=255|,
> the |next_char| byte is the so-called right boundary character of this font;
> the value of |next_char| need not lie between |bc| and~|ec|.
> If the very last instruction of the |lig_kern| array has |skip_byte=255|,
> there is a special ligature/kerning program for a left boundary character,
> beginning at location |256*op_byte+remainder|.
> The interpretation is that \TeX\ puts implicit boundary characters
> before and after each consecutive string of characters from the same font.
> These implicit characters do not appear in the output, but they can affect
> ligatures and kerning.
>
> If the very first instruction of a character's |lig_kern| program has
> |skip_byte>128|, the program actually begins in location
> |256*op_byte+remainder|. This feature allows access to large |lig_kern|
> arrays, because the first instruction must otherwise
> appear in a location |<=255|.
>
> Any instruction with |skip_byte>128| in the |lig_kern| array must satisfy
> the condition
> $$\hbox{|256*op_byte+remainder<nl|.}$$
> If such an instruction is encountered during
> normal program execution, it denotes an unconditional halt; no ligature
> or kerning command is performed.
>
> @d stop_flag==qi(128) {value indicating `\.{STOP}' in a lig/kern program}
> @d kern_flag==qi(128) {op code for a kern step}
> @d skip_byte(#)==#.b0
8325c10527
< @d op_bit(#)==#.b2
---
> @d op_byte(#)==#.b2
8337,8338c10539,10540
< $TR^kMR^kB$ from top to bottom, for some |k≥0|, unless $M$ is absent;
< in the latter case we can have $TR^kB$ for both even and odd values of@@|k|.
---
> $TR^kMR^kB$ from top to bottom, for some |k>=0|, unless $M$ is absent;
> in the latter case we can have $TR^kB$ for both even and odd values of~|k|.
8354c10556
< number; it is the only |fix_word| other than the design size itself that is
---
> number; it's the only |fix_word| other than the design size itself that is
8358c10560
< Note that character @'40 in the font need not have anything to do with
---
> Note that character |" "| in the font need not have anything to do with
8392,8394c10594,10597
< When the user defines \.{\\font 100}, say, \TeX\ assigns an
< internal number to the user's font 100. For example, if this internal number
< is 13, we will have |font_code[13]=100| and |font_number[100]=13|.
---
> When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number
> to the user's font~\.{\\f}. Adding this number to |font_id_base| gives the
> |eqtb| location of a ``frozen'' control sequence that will always select
> the font.
8397d10599
< @!user_font_code=0..bad_font_code; {font identification in \TeX\ input}
8398a10601
> @!font_index=0..font_mem_size; {index into |font_info|}
8401a10605,10607
> @d non_char==qi(256) {a |halfword| code that can't match a real character}
> @d non_address=0 {a spurious |bchar_label|}
>
8403,8405c10609,10611
< @!font_info:array[0..font_mem_size] of memory_word;
< {the big collection of font data}
< @!fmem_ptr:0..font_mem_size; {first unused word of |font_info|}
---
> @!font_info:array[font_index] of memory_word;
> {the big collection of font data}
> @!fmem_ptr:font_index; {first unused word of |font_info|}
8407,8410d10612
< @!font_code:array[internal_font_number] of user_font_code; {the first user
< font code corresponding to an internal font number}
< @!font_number:array[user_font_code] of internal_font_number; {the internal
< font number corresponding to a user's font code}
8413,8416c10615,10617
< @!font_scaled:array[internal_font_number] of boolean; {is the ``at'' size
< different from the design size?}
< @!font_params:array[internal_font_number] of halfword; {how many font
< parameters are present}
---
> @!font_dsize:array[internal_font_number] of scaled; {``design'' size}
> @!font_params:array[internal_font_number] of font_index; {how many font
> parameters are present}
8419,8426c10620,10638
< @!font_bc:array[internal_font_number] of eight_bits; {beginning (smallest)
< character code}
< @!font_ec:array[internal_font_number] of eight_bits; {ending (largest)
< character code}
< @!font_glue:array[internal_font_number] of pointer; {glue specification
< for interword space, |null| if not allocated}
< @!font_used:array[internal_font_number] of boolean; {has a character from
< this font actually appeared in the output?}
---
> @!font_bc:array[internal_font_number] of eight_bits;
> {beginning (smallest) character code}
> @!font_ec:array[internal_font_number] of eight_bits;
> {ending (largest) character code}
> @!font_glue:array[internal_font_number] of pointer;
> {glue specification for interword space, |null| if not allocated}
> @!font_used:array[internal_font_number] of boolean;
> {has a character from this font actually appeared in the output?}
> @!hyphen_char:array[internal_font_number] of integer;
> {current \.{\\hyphenchar} values}
> @!skew_char:array[internal_font_number] of integer;
> {current \.{\\skewchar} values}
> @!bchar_label:array[internal_font_number] of font_index;
> {start of |lig_kern| program for left boundary character,
> |non_address| if there is none}
> @!font_bchar:array[internal_font_number] of min_quarterword..non_char;
> {right boundary character, |non_char| if there is none}
> @!font_false_bchar:array[internal_font_number] of min_quarterword..non_char;
> {|font_bchar| if it doesn't exist in the font, otherwise |non_char|}
8432,8435c10644,10647
< part of this word (the |b0| field), |font_info[width_base[f]+w].sc|
< is the width of the character. (These formulas assume that |min_quarterword|
< has already been added to |c| and to |w|, since \TeX\ stores its
< quarterwords that way.)
---
> part of this word (the |b0| field), the width of the character is
> |font_info[width_base[f]+w].sc|. (These formulas assume that
> |min_quarterword| has already been added to |c| and to |w|, since \TeX\
> stores its quarterwords that way.)
8437c10649
< @<Globals...@>=
---
> @<Glob...@>=
8439c10651
< {base addresses for |char_info|}
---
> {base addresses for |char_info|}
8441c10653
< {base addresses for widths}
---
> {base addresses for widths}
8443c10655
< {base addresses for heights}
---
> {base addresses for heights}
8445c10657
< {base addresses for depths}
---
> {base addresses for depths}
8447c10659
< {base addresses for italic corrections}
---
> {base addresses for italic corrections}
8449c10661
< {base addresses for ligature/kerning programs}
---
> {base addresses for ligature/kerning programs}
8451c10663
< {base addresses for kerns}
---
> {base addresses for kerns}
8453c10665
< {base addresses for extensible recipes}
---
> {base addresses for extensible recipes}
8455c10667
< {base addresses for font parameters}
---
> {base addresses for font parameters}
8457,8458c10669,10670
< @ In order to cope more easily with erroneous situations, the undefined font
< is assumed to have seven valid parameters, all equal to zero.
---
> @ @<Set init...@>=
> for k:=font_base to font_max do font_used[k]:=false;
8460,8467c10672,10694
< @<Set init...@>=
< for k←0 to bad_font_code do font_number[k]←undefined_font;
< font_ptr←font_base; fmem_ptr←7; font_code[undefined_font]←bad_font_code;
< font_bc[undefined_font]←1; font_ec[undefined_font]←0;
< font_glue[undefined_font]←zero_glue; font_params[undefined_font]←7;
< param_base[undefined_font]←-1;
< for k←0 to 6 do font_info[k].sc←0;
< for k←font_base to font_max do font_used[k]←false;
---
> @ \TeX\ always knows at least one font, namely the null font. It has no
> characters, and its seven parameters are all equal to zero.
>
> @<Initialize table...@>=
> font_ptr:=null_font; fmem_ptr:=7;
> font_name[null_font]:="nullfont"; font_area[null_font]:="";
> hyphen_char[null_font]:="-"; skew_char[null_font]:=-1;
> bchar_label[null_font]:=non_address;
> font_bchar[null_font]:=non_char; font_false_bchar[null_font]:=non_char;
> font_bc[null_font]:=1; font_ec[null_font]:=0;
> font_size[null_font]:=0; font_dsize[null_font]:=0;
> char_base[null_font]:=0; width_base[null_font]:=0;
> height_base[null_font]:=0; depth_base[null_font]:=0;
> italic_base[null_font]:=0; lig_kern_base[null_font]:=0;
> kern_base[null_font]:=0; exten_base[null_font]:=0;
> font_glue[null_font]:=null; font_params[null_font]:=7;
> param_base[null_font]:=-1;
> for k:=0 to 6 do font_info[k].sc:=0;
>
> @ @<Put each...@>=
> primitive("nullfont",set_font,null_font);
> @!@:null_font_}{\.{\\nullfont} primitive@>
> text(frozen_null_font):="nullfont"; eqtb[frozen_null_font]:=eqtb[cur_val];
8475c10702
< the character's width, so that the long formula above is at least
---
> the character's width; hence the long formula above is at least
8493,8495c10720,10722
< Access to a character's |height|, |depth|, and |tag| fields is part of
< \TeX's inner loop, so we want these macros to produce code that is as fast
< as possible under the circumstances.
---
> Access to a character's |width|, |height|, |depth|, and |tag| fields is
> part of \TeX's inner loop, so we want these macros to produce code that is
> as fast as possible under the circumstances.
8503c10730
< @d char_italic_end(#)==qo(#.b2) div 4].sc
---
> @d char_italic_end(#)==(qo(#.b2)) div 4].sc
8506c10733
< @d char_height_end(#)==# div 16].sc
---
> @d char_height_end(#)==(#) div 16].sc
8508c10735
< @d char_depth_end(#)==# mod 16].sc
---
> @d char_depth_end(#)==(#) mod 16].sc
8510c10737
< @d char_tag(#)==(qo(#.b2) mod 4)
---
> @d char_tag(#)==((qo(#.b2)) mod 4)
8520,8521c10747,10748
< null_character.b0←min_quarterword; null_character.b1←min_quarterword;
< null_character.b2←min_quarterword; null_character.b3←min_quarterword;
---
> null_character.b0:=min_quarterword; null_character.b1:=min_quarterword;
> null_character.b2:=min_quarterword; null_character.b3:=min_quarterword;
8525c10752,10755
< kerning command |j|.
---
> kerning command~|j| in font~|f|. If |j| is the |char_info| for a character
> with a ligature/kern program, the first instruction of that program is either
> |i=font_info[lig_kern_start(f)(j)]| or |font_info[lig_kern_restart(f)(i)]|,
> depending on whether or not |skip_byte(i)<=stop_flag|.
8527,8528c10757,10761
< @d lig_kern_start(#)==lig_kern_base[#]+rem_byte {beginning of lig/kern program}
< @d char_kern_end(#)==rem_byte(#)].sc
---
> The constant |kern_base_offset| should be simplified, for \PASCAL\ compilers
> that do not do local optimization.
> @^system dependencies@>
>
> @d char_kern_end(#)==256*op_byte(#)+rem_byte(#)].sc
8529a10763,10766
> @d kern_base_offset==256*(128+min_quarterword)
> @d lig_kern_start(#)==lig_kern_base[#]+rem_byte {beginning of lig/kern program}
> @d lig_kern_restart_end(#)==256*op_byte(#)+rem_byte(#)+32768-kern_base_offset
> @d lig_kern_restart(#)==lig_kern_base[#]+lig_kern_restart_end
8550,8554c10787,10793
< is called |read_font_info|. It has four parameters: the user font code@@|u|,
< the file name and area strings |nom| and |aire|, and the ``at'' size@@|s|;
< if |s|@@is nonzero, |read_font_info| substitutes |s| for the design size.
< In the latter case, |s| must be positive and less than 2048 (i.e.,
< it is less than $2^{27}$ when considered as an integer).
---
> is called |read_font_info|. It has four parameters: the user font
> identifier~|u|, the file name and area strings |nom| and |aire|, and the
> ``at'' size~|s|. If |s|~is negative, it's the negative of a scale factor
> to be applied to the design size; |s=-1000| is the normal case.
> Otherwise |s| will be substituted for the design size; in this
> case, |s| must be positive and less than $2048\rm\,pt$
> (i.e., it must be less than $2^{27}$ when considered as an integer).
8556a10796
> It returns the value of the internal font number that was just loaded.
8558c10798
< information is stored.
---
> information is stored; |null_font| is returned in this case.
8563,8564c10803,10804
< @p procedure read_font_info(@!u:user_font_code;
< @!nom,@!aire:str_number; @!s:scaled); {input a \.{TFM} file}
---
> @p function read_font_info(@!u:pointer;@!nom,@!aire:str_number;
> @!s:scaled):internal_font_number; {input a \.{TFM} file}
8566c10806
< var k:0..font_mem_size; {index into |font_info|}
---
> var k:font_index; {index into |font_info|}
8569c10809
< {sizes of subfiles}
---
> {sizes of subfiles}
8570a10811
> @!g:internal_font_number; {the number to return}
8572a10814,10815
> @!bch_label:integer; {left boundary start location, or infinity}
> @!bchar:0..256; {right boundary character, or 256}
8575,8578c10818,10822
< {auxiliary quantities used in fixed-point multiplication}
< begin @<Read and check the font data; |abort| if the \.{TFM} file is
< malformed; if there's no room for this font, say so and |goto
< done|; otherwise |incr(font_ptr)| and |goto done|@>;
---
> {auxiliary quantities used in fixed-point multiplication}
> begin g:=null_font;@/
> @<Read and check the font data; |abort| if the \.{TFM} file is
> malformed; if there's no room for this font, say so and |goto
> done|; otherwise |incr(font_ptr)| and |goto done|@>;
8580c10824,10825
< done: b_close(tfm_file);
---
> done: if file_opened then b_close(tfm_file);
> read_font_info:=g;
8590,8594c10835,10842
< @d start_font_error_message==print_nl("! Font "); print_int(u);
< print_char("="); print_file_name(nom,aire,"");
< if s≠0 then
< begin print(" at "); print_scaled(s); print("pt");
< end
---
> @d start_font_error_message==print_err("Font "); sprint_cs(u);
> print_char("="); print_file_name(nom,aire,"");
> if s>=0 then
> begin print(" at "); print_scaled(s); print("pt");
> end
> else if s<>-1000 then
> begin print(" scaled "); print_int(-s);
> end
8605c10853
< ("e.g., type `I\font<same number>=<new name>'.");
---
> ("e.g., type `I\font<same font id>=<substitute font name>'.");
8621c10869
< file_opened←false;
---
> file_opened:=false;
8624,8625c10872,10873
< if not b_open_in(tfm_file) then goto bad_tfm;
< file_opened←true
---
> if not b_open_in(tfm_file) then abort;
> file_opened:=true
8631c10879
< for example by defining |fget| to be `\!|begin get(tfm_file);|
---
> for example by defining |fget| to be `\ignorespaces|begin get(tfm_file);|
8637,8646c10885,10894
< @d read_sixteen(#)==begin #←fbyte;
< if #>127 then abort;
< fget; #←#*@'400+fbyte;
< end
< @d store_four_quarters(#)==begin fget; a←fbyte; qw.b0←qi(a);
< fget; b←fbyte; qw.b1←qi(b);
< fget; c←fbyte; qw.b2←qi(c);
< fget; d←fbyte; qw.b3←qi(d);
< #←qw;
< end
---
> @d read_sixteen(#)==begin #:=fbyte;
> if #>127 then abort;
> fget; #:=#*@'400+fbyte;
> end
> @d store_four_quarters(#)==begin fget; a:=fbyte; qw.b0:=qi(a);
> fget; b:=fbyte; qw.b1:=qi(b);
> fget; c:=fbyte; qw.b2:=qi(c);
> fget; d:=fbyte; qw.b3:=qi(d);
> #:=qw;
> end
8653c10901,10904
< if (bc>ec+1)∨(ec>255) then abort;
---
> if (bc>ec+1)or(ec>255) then abort;
> if bc>255 then {|bc=256| and |ec=255|}
> begin bc:=1; ec:=0;
> end;
8662c10913,10914
< if lf≠6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then abort;
---
> if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then abort;
> if (nw=0)or(nh=0)or(nd=0)or(ni=0) then abort;
8665c10917
< @ The preliminary settings of the index variables |char_base|,
---
> @ The preliminary settings of the index-offset variables |char_base|,
8672,8685c10924,10937
< lf←lf-6-lh; {|lf| words should be loaded into |font_info|}
< if np<7 then lf←lf+7-np; {at least seven parameters will appear}
< if (font_ptr=font_max)∨(fmem_ptr+lf>font_mem_size) then
< @<Apologize for not loading the font, |goto done|@>;
< f←font_ptr+1;
< char_base[f]←fmem_ptr-bc;
< width_base[f]←char_base[f]+ec+1;
< height_base[f]←width_base[f]+nw;
< depth_base[f]←height_base[f]+nh;
< italic_base[f]←depth_base[f]+nd;
< lig_kern_base[f]←italic_base[f]+ni;
< kern_base[f]←lig_kern_base[f]+nl;
< exten_base[f]←kern_base[f]+nk;
< param_base[f]←exten_base[f]+ne
---
> lf:=lf-6-lh; {|lf| words should be loaded into |font_info|}
> if np<7 then lf:=lf+7-np; {at least seven parameters will appear}
> if (font_ptr=font_max)or(fmem_ptr+lf>font_mem_size) then
> @<Apologize for not loading the font, |goto done|@>;
> f:=font_ptr+1;
> char_base[f]:=fmem_ptr-bc;
> width_base[f]:=char_base[f]+ec+1;
> height_base[f]:=width_base[f]+nw;
> depth_base[f]:=height_base[f]+nh;
> italic_base[f]:=depth_base[f]+nd;
> lig_kern_base[f]:=italic_base[f]+ni;
> kern_base[f]:=lig_kern_base[f]+nl-kern_base_offset;
> exten_base[f]:=kern_base[f]+kern_base_offset+nk;
> param_base[f]:=exten_base[f]+ne
8689c10941
< print(" not loaded: No more room.");
---
> print(" not loaded: Not enough room left");
8694c10946
< ("Or maybe try `I\font<same number>=<name of loaded font>'.");
---
> ("Or maybe try `I\font<same font id>=<name of loaded font>'.");
8704c10956
< fget; z←z*@'400+fbyte; fget; z←(z*@'20)+(fbyte div@'20);
---
> fget; z:=z*@'400+fbyte; fget; z:=(z*@'20)+(fbyte div@'20);
8707,8713c10959,10965
< begin fget;fget;fget;fget;decr(lh); {ignore the rest of the header}
< end;
< if s=0 then font_scaled[f]←false
< else if z=s then font_scaled[f]←false
< else begin font_scaled[f]←true; z←s;
< end;
< font_size[f]←z;
---
> begin fget;fget;fget;fget;decr(lh); {ignore the rest of the header}
> end;
> font_dsize[f]:=z;
> if s<>-1000 then
> if s>=0 then z:=s
> else z:=xn_over_d(z,-s,1000);
> font_size[f]:=z;
8717,8727c10969,10979
< for k←fmem_ptr to width_base[f]-1 do
< begin store_four_quarters(font_info[k].qqqq);
< if (a≥nw)∨(b div @'20≥nh)∨(b mod @'20≥nd)∨
< (c div 4≥ni) then abort;
< case c mod 4 of
< lig_tag: if d≥nl then abort;
< ext_tag: if d≥ne then abort;
< list_tag: @<Check for charlist cycle@>;
< othercases do_nothing {|no_tag|}
< endcases;
< end
---
> for k:=fmem_ptr to width_base[f]-1 do
> begin store_four_quarters(font_info[k].qqqq);
> if (a>=nw)or(b div @'20>=nh)or(b mod @'20>=nd)or
> (c div 4>=ni) then abort;
> case c mod 4 of
> lig_tag: if d>=nl then abort;
> ext_tag: if d>=ne then abort;
> list_tag: @<Check for charlist cycle@>;
> othercases do_nothing {|no_tag|}
> endcases;
> end
8734c10986
< @d check_byte_range(#)==begin if (#<bc)∨(#>ec) then abort@+end
---
> @d check_byte_range(#)==begin if (#<bc)or(#>ec) then abort@+end
8740,8744c10992,10996
< begin qw←char_info(f)(d);
< {N.B.: not |qi(d)|, since |char_base[f]| hasn't been adjusted yet}
< if char_tag(qw)≠list_tag then goto not_found;
< d←qo(rem_byte(qw)); {next character on the list}
< end;
---
> begin qw:=char_info(f)(d);
> {N.B.: not |qi(d)|, since |char_base[f]| hasn't been adjusted yet}
> if char_tag(qw)<>list_tag then goto not_found;
> d:=qo(rem_byte(qw)); {next character on the list}
> end;
8750c11002
< $$x=\left\{\vcenter{\halign{\lft{$#$,}\qquad&if \lft{$#$}\cr
---
> $$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
8753c11005
< (No other choices of $a$ are allowed, since the magnitude of a number in
---
> (No other choices of |a| are allowed, since the magnitude of a number in
8755,8756c11007,11008
< quantity by the integer@@|z|, which is known to be less than $2^{27}$. Let
< $\alpha=16z$. If $|z|<2^{23}$, the individual multiplications $b\cdot z$,
---
> quantity by the integer~|z|, which is known to be less than $2^{27}$.
> If $|z|<2^{23}$, the individual multiplications $b\cdot z$,
8762c11014
< if $a=0$, or the same quantity minus $\alpha$ if $a=255$.
---
> if $a=0$, or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$.
8766,8770c11018,11022
< @d store_scaled(#)==begin fget; a←fbyte; fget; b←fbyte;
< fget; c←fbyte; fget; d←fbyte;@/
< sw←(((((d*z)div@'400)+(c*z))div@'400)+(b*z))div beta;
< if a=0 then #←sw@+else if a=255 then #←sw-alpha@+else abort;
< end
---
> @d store_scaled(#)==begin fget; a:=fbyte; fget; b:=fbyte;
> fget; c:=fbyte; fget; d:=fbyte;@/
> sw:=(((((d*z)div@'400)+(c*z))div@'400)+(b*z))div beta;
> if a=0 then #:=sw@+else if a=255 then #:=sw-alpha@+else abort;
> end
8774,8779c11026,11031
< for k←width_base[f] to lig_kern_base[f]-1 do
< store_scaled(font_info[k].sc);
< if font_info[width_base[f]].sc≠0 then abort; {\\{width}[0] must be zero}
< if font_info[height_base[f]].sc≠0 then abort; {\\{height}[0] must be zero}
< if font_info[depth_base[f]].sc≠0 then abort; {\\{depth}[0] must be zero}
< if font_info[italic_base[f]].sc≠0 then abort; {\\{italic}[0] must be zero}
---
> for k:=width_base[f] to lig_kern_base[f]-1 do
> store_scaled(font_info[k].sc);
> if font_info[width_base[f]].sc<>0 then abort; {\\{width}[0] must be zero}
> if font_info[height_base[f]].sc<>0 then abort; {\\{height}[0] must be zero}
> if font_info[depth_base[f]].sc<>0 then abort; {\\{depth}[0] must be zero}
> if font_info[italic_base[f]].sc<>0 then abort; {\\{italic}[0] must be zero}
8783,8799c11035,11066
< begin alpha←16*z; beta←16;
< while z≥@'40000000 do
< begin z←z div 2; beta←beta div 2;
< end;
< end
<
< @ @<Read ligature/kern program@>=
< begin for k←lig_kern_base[f] to kern_base[f]-1 do
< begin store_four_quarters(font_info[k].qqqq);
< check_byte_range(b);
< if c<kern_flag then check_byte_range(d)
< else if d≥nk then abort;
< end;
< if (nl>0)∧(a<stop_flag) then abort; {check for stop bit on last command}
< for k←kern_base[f] to exten_base[f]-1 do
< store_scaled(font_info[k].sc);
< end
---
> begin alpha:=16;
> while z>=@'40000000 do
> begin z:=z div 2; alpha:=alpha+alpha;
> end;
> beta:=256 div alpha; alpha:=alpha*z;
> end
>
> @ @d check_existence(#)==@t@>@;@/
> begin check_byte_range(#);
> qw:=char_info(f)(#); {N.B.: not |qi(#)|}
> if not char_exists(qw) then abort;
> end
>
> @<Read ligature/kern program@>=
> bch_label:=@'77777; bchar:=256;
> if nl>0 then
> begin for k:=lig_kern_base[f] to kern_base[f]+kern_base_offset-1 do
> begin store_four_quarters(font_info[k].qqqq);
> if a>128 then
> begin if 256*c+d>=nl then abort;
> if a=255 then if k=lig_kern_base[f] then bchar:=b;
> end
> else begin if b<>bchar then check_existence(b);
> if c<128 then check_existence(d) {check ligature}
> else if 256*(c-128)+d>=nk then abort; {check kern}
> if a<128 then if k-lig_kern_base[f]+a+1>=nl then abort;
> end;
> end;
> if a=255 then bch_label:=256*c+d;
> end;
> for k:=kern_base[f]+kern_base_offset to exten_base[f]-1 do
> store_scaled(font_info[k].sc);
8802,8808c11069,11075
< for k←exten_base[f] to param_base[f]-1 do
< begin store_four_quarters(font_info[k].qqqq);
< if a≠0 then check_byte_range(a);
< if b≠0 then check_byte_range(b);
< if c≠0 then check_byte_range(c);
< check_byte_range(d);
< end
---
> for k:=exten_base[f] to param_base[f]-1 do
> begin store_four_quarters(font_info[k].qqqq);
> if a<>0 then check_existence(a);
> if b<>0 then check_existence(b);
> if c<>0 then check_existence(c);
> check_existence(d);
> end
8814,8821c11081,11088
< begin for k←1 to np do
< if k=1 then {the |slant| parameter is a pure number}
< begin fget; sw←fbyte; if sw>127 then sw←sw-256;
< fget; sw←sw*@'400+fbyte; fget; sw←sw*@'400+fbyte;
< fget; font_info[param_base[f]].sc←
< (sw*@'20)+(fbyte div@'20);
< end
< else store_scaled(font_info[param_base[f]+k-1].sc);
---
> begin for k:=1 to np do
> if k=1 then {the |slant| parameter is a pure number}
> begin fget; sw:=fbyte; if sw>127 then sw:=sw-256;
> fget; sw:=sw*@'400+fbyte; fget; sw:=sw*@'400+fbyte;
> fget; font_info[param_base[f]].sc:=
> (sw*@'20)+(fbyte div@'20);
> end
> else store_scaled(font_info[param_base[f]+k-1].sc);
8823c11090
< for k←np+1 to 7 do font_info[param_base[f]+k-1].sc←0;
---
> for k:=np+1 to 7 do font_info[param_base[f]+k-1].sc:=0;
8830,8831c11097,11098
< @d adjust(#)==#[f]←qo(#[f])
< {correct for the excess |min_quarterword| that was added}
---
> @d adjust(#)==#[f]:=qo(#[f])
> {correct for the excess |min_quarterword| that was added}
8834,8838c11101,11113
< font_code[f]←u; font_number[u]←f;
< if np≥7 then font_params[f]←np@+else font_params[f]←7;
< font_name[f]←nom;
< font_area[f]←aire;
< font_bc[f]←bc; font_ec[f]←ec; font_glue[f]←null;
---
> if np>=7 then font_params[f]:=np@+else font_params[f]:=7;
> hyphen_char[f]:=default_hyphen_char; skew_char[f]:=default_skew_char;
> if bch_label<nl then bchar_label[f]:=bch_label+lig_kern_base[f]
> else bchar_label[f]:=non_address;
> font_bchar[f]:=qi(bchar);
> font_false_bchar[f]:=qi(bchar);
> if bchar<=ec then if bchar>=bc then
> begin qw:=char_info(f)(bchar); {N.B.: not |qi(bchar)|}
> if char_exists(qw) then font_false_bchar[f]:=non_char;
> end;
> font_name[f]:=nom;
> font_area[f]:=aire;
> font_bc[f]:=bc; font_ec[f]:=ec; font_glue[f]:=null;
8842c11117
< fmem_ptr←fmem_ptr+lf; font_ptr←f; goto done
---
> fmem_ptr:=fmem_ptr+lf; font_ptr:=f; g:=f; goto done
8847,8848c11122,11123
< @<Declare procedures that scan restricted classes of integers@>=
< procedure scan_font_number;
---
> @<Declare procedures that scan font-related stuff@>=
> procedure scan_font_ident;
8850,8866c11125,11139
< begin scan_int;
< if (cur_val<0)∨(cur_val≥bad_font_code) then f←undefined_font
< else f←font_number[cur_val];
< if f=undefined_font then
< begin print_nl("! Undefined font code");
< @.Undefined font code@>
< help3("Watch out---you must define a font code before you")@/
< ("try to use the font, so you may have to start over.")@/
< ("(Chapter 27 of the manual explains how to survive this.)");
< int_error(cur_val);
< end;
< cur_val←f;
< end;
<
< @ The following routine is used to implement `\.{\\texinfo} |m| |n|'. If
< font |m| is undefined, no error message is needed since |scan_font_number|
< will have already issued one.
---
> @!m:halfword;
> begin @<Get the next non-blank non-call...@>;
> if cur_cmd=def_font then f:=cur_font
> else if cur_cmd=set_font then f:=cur_chr
> else if cur_cmd=def_family then
> begin m:=cur_chr; scan_four_bit_int; f:=equiv(m+cur_val);
> end
> else begin print_err("Missing font identifier");
> @.Missing font identifier@>
> help2("I was looking for a control sequence whose")@/
> ("current meaning has been defined by \font.");
> back_error; f:=null_font;
> end;
> cur_val:=f;
> end;
8867a11141
> @ The following routine is used to implement `\.{\\fontdimen} |n| |f|'.
8871,8873c11145,11147
< @<Declare procedures that scan restricted classes of integers@>=
< procedure scan_tex_info(@!writing:boolean);
< {sets |cur_val| to |font_info| location}
---
> @<Declare procedures that scan font-related stuff@>=
> procedure find_font_dimen(@!writing:boolean);
> {sets |cur_val| to |font_info| location}
8875,8889c11149,11162
< begin scan_font_number; f←cur_val; scan_int;
< if f=undefined_font then cur_val←fmem_ptr {a harmless location}
< else begin if cur_val≤0 then cur_val←fmem_ptr
< else begin if writing ∧(cur_val≤space_shrink_code)∧@|
< (cur_val≥space_code)∧(font_glue[f]≠null) then
< begin delete_glue_ref(font_glue[f]);
< font_glue[f]←null;
< end;
< if cur_val>font_params[f] then
< if f<font_ptr then cur_val←fmem_ptr
< else @<Increase the number of parameters in the last font@>
< else cur_val←cur_val+param_base[f];
< end;
< @<Issue an error message if |cur_val=fmem_ptr|@>;
< end;
---
> @!n:integer; {the parameter number}
> begin scan_int; n:=cur_val; scan_font_ident; f:=cur_val;
> if n<=0 then cur_val:=fmem_ptr
> else begin if writing and(n<=space_shrink_code)and@|
> (n>=space_code)and(font_glue[f]<>null) then
> begin delete_glue_ref(font_glue[f]);
> font_glue[f]:=null;
> end;
> if n>font_params[f] then
> if f<font_ptr then cur_val:=fmem_ptr
> else @<Increase the number of parameters in the last font@>
> else cur_val:=n+param_base[f];
> end;
> @<Issue an error message if |cur_val=fmem_ptr|@>;
8894,8900c11167,11174
< begin help2("To increase the number of font parameters, you must")@/
< ("use \texinfo immediately after the font code is defined.");
< print_nl("! Font "); print_int(font_code[f]);
< @.Font x has n texinfo...@>
< print(" has "); print_int(font_params[f]);
< print(" texinfo parameters"); error;
< end
---
> begin print_err("Font "); print_esc(font_id_text(f));
> print(" has only "); print_int(font_params[f]);
> print(" fontdimen parameters");
> @.Font x has only...@>
> help2("To increase the number of font parameters, you must")@/
> ("use \fontdimen immediately after the \font is loaded.");
> error;
> end
8903,8905c11177,11183
< repeat if fmem_ptr=font_mem_size then overflow("font memory",font_mem_size);
< font_info[fmem_ptr].sc←0; incr(fmem_ptr); incr(font_params[f]);
< until cur_val=font_params[f]
---
> begin repeat if fmem_ptr=font_mem_size then
> overflow("font memory",font_mem_size);
> @:TeX capacity exceeded font memory}{\quad font memory@>
> font_info[fmem_ptr].sc:=0; incr(fmem_ptr); incr(font_params[f]);
> until n=font_params[f];
> cur_val:=fmem_ptr-1; {this equals |param_base[f]+font_params[f]|}
> end
8909c11187
< that characters exist when it sees them. The following procedure
---
> that characters exist when it sees them. The following procedure
8913,8915c11191,11193
< begin if tracing_lost_chars≠0 then
< begin begin_diagnostic;
< print_nl("Missing character: There is no ");
---
> begin if tracing_lost_chars>0 then
> begin begin_diagnostic;
> print_nl("Missing character: There is no ");
8917,8919c11195,11197
< print_ascii(c); print(" in font ");
< print(font_name[f]); print_char("!"); end_diagnostic;
< end;
---
> print_ASCII(c); print(" in font ");
> slow_print(font_name[f]); print_char("!"); end_diagnostic(false);
> end;
8929,8933c11207,11211
< begin if (font_bc[f]≤c)∧(font_ec[f]≥c) then
< if char_exists(char_info(f)(qi(c))) then
< begin p←get_avail; font(p)←f; character(p)←qi(c);
< new_character←p; return;
< end;
---
> begin if font_bc[f]<=c then if font_ec[f]>=c then
> if char_exists(char_info(f)(qi(c))) then
> begin p:=get_avail; font(p):=f; character(p):=qi(c);
> new_character:=p; return;
> end;
8935c11213
< new_character←null;
---
> new_character:=null;
8937,8938c11215,11217
< @* \[30] Device-independent file format.
< The most important output pro\-duced by a run of \TeX\ is the ``device
---
>
> @* \[31] Device-independent file format.
> The most important output produced by a run of \TeX\ is the ``device
8941c11220
< David R. Fuchs in 1979. Almost any reasonable device can be driven by
---
> David R. Fuchs in 1979. Almost any reasonable typesetting device can be
8944c11223
< a program that takes \.{DVI} files as input, and dozens of such
---
> driven by a program that takes \.{DVI} files as input, and dozens of such
8959c11238,11239
< $-2^{15}$ and $2^{15}-1$.
---
> $-2^{15}$ and $2^{15}-1$. As in \.{TFM} files, numbers that occupy
> more than one byte position appear in BigEndian order.
8961,8969c11241,11252
< A \.{DVI} file consists of a sequence of one or more ``pages,'' followed by
< a ``postamble.'' A ``page'' consists of a |bop| command, followed by any number
< of other commands that tell where characters are to be placed on a physical
< page, followed by an |eop| command. The pages appear in the order that \TeX\
< generated them. If we ignore |nop| commands (which are allowed between
< any two commands in the file), each |eop| command is immediately followed by
< a |bop| command, or by a |pst| command; in the latter case, there are no
< more pages in the file, and the remaining bytes form the postamble.
< Further details about the postamble will be explained later.
---
> A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
> or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
> |pre| command, with its parameters that define the dimensions used in the
> file; this must come first. Each ``page'' consists of a |bop| command,
> followed by any number of other commands that tell where characters are to
> be placed on a physical page, followed by an |eop| command. The pages
> appear in the order that \TeX\ generated them. If we ignore |nop| commands
> and \\{fnt\_def} commands (which are allowed between any two commands in
> the file), each |eop| command is immediately followed by a |bop| command,
> or by a |post| command; in the latter case, there are no more pages in the
> file, and the remaining bytes form the postamble. Further details about
> the postamble will be explained later.
8973c11256
< the first byte is number@@0, then comes number@@1, and so on. For example,
---
> the first byte is number~0, then comes number~1, and so on. For example,
8975,8980c11258,11264
< this makes it feasible to read the pages in backwards order, in case you
< are producing output on devices that stack their output face up. If the
< first page on a \.{DVI} file occupies bytes 0 to 99, and if the second
< page occupies bytes 100 to 299, then the |bop| that starts in byte 100
< points to 0 and the |bop| thats starts in byte 300 points to 100. (The
< first |bop|, i.e., the one that starts in byte 0, has a pointer of $-1$.)
---
> this makes it feasible to read the pages in backwards order, in case the
> results are being directed to a device that stacks its output face up.
> Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
> first page occupies bytes 100 to 999, say, and if the second
> page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
> points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
> very first |bop|, i.e., the one starting in byte 100, has a pointer of~$-1$.)
8984,8985c11268,11269
< implicit instead of explicit; when a \.{DVI}-reading program reads the
< commands for a page, it keeps track of several quantities: (a)@@The current
---
> implicit instead of explicit. When a \.{DVI}-reading program reads the
> commands for a page, it keeps track of several quantities: (a)~The current
8987c11271
< by \\{fnt} and \\{fnt\_num} commands. (b)@@The current position on the page
---
> by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
8993c11277
< flipped; the Cartesian version of |(h,v)| would be |(h,-v)|. (c)@@The
---
> flipped; the Cartesian version of |(h,v)| would be |(h,-v)|. (c)~The
8995,8996c11279,11280
< where |w| and@@|x| are used for horizontal spacing and where |y| and@@|z|
< are used for vertical spacing. (d)@@There is a stack containing
---
> where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
> are used for vertical spacing. (d)~There is a stack containing
8998c11282
< change the current level of operation. Note that the current font@@|f| is
---
> change the current level of operation. Note that the current font~|f| is
9004c11288
< there is a small unit of measurement such that increasing |h| by@@1 means
---
> there is a small unit of measurement such that increasing |h| by~1 means
9010,9012c11294,11296
< @ Here is list of all the commands that may appear in a \.{DVI} file. With
< each command we give its symbolic name (e.g., |bop|), its opcode byte
< (e.g., 129), and its parameters (if any). The parameters are followed
---
> @ Here is a list of all the commands that may appear in a \.{DVI} file. Each
> command is specified by its symbolic name (e.g., |bop|), its opcode byte
> (e.g., 139), and its parameters (if any). The parameters are followed
9016c11300
< \yskip\hang|set_char_0| 0. Typeset character number@@0 from font@@|f|
---
> \yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
9022,9024c11306,11308
< \yskip\hang|set_char_1| through |set_char_127| (opcodes 1 to 127).
< Do the operations of |set_char_0|, but use the appropriate character number
< instead of char\-acter@@0.
---
> \yskip\hang\\{set\_char\_1} through \\{set\_char\_127} (opcodes 1 to 127).
> Do the operations of |set_char_0|; but use the character whose number
> matches the opcode, instead of character~0.
9027,9028c11311,11312
< number@@|c| is typeset. \TeX82 uses this command for characters in the
< range |128≤c<256|.
---
> number~|c| is typeset. \TeX82 uses this command for characters in the
> range |128<=c<256|.
9030,9031c11314,11315
< \yskip\hang|set2| 129 |c[2]|. Same as |set1|, except that@@|c| is two
< bytes long, so it is in the range |0≤c<65536|. \TeX82 never uses this
---
> \yskip\hang|@!set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
> bytes long, so it is in the range |0<=c<65536|. \TeX82 never uses this
9036c11320
< \yskip\hang|set3| 130 |c[3]|. Same as |set1|, except that@@|c| is three
---
> \yskip\hang|@!set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
9041c11325
< \yskip\hang|set4| 131 |c[4]|. Same as |set1|, except that@@|c| is four
---
> \yskip\hang|@!set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
9045,9046c11329,11330
< of height@@|a| and width@@|b|, with its bottom left corner at |(h,v)|. Then
< set |h←h+b|. If either |a≤0| or |b≤0|, nothing should be typeset. Note
---
> of height~|a| and width~|b|, with its bottom left corner at |(h,v)|. Then
> set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note
9051c11335
< \yskip\hang|put1| 133 |c[1]|. Typeset character number@@|c| from font@@|f|
---
> \yskip\hang|@!put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
9056c11340
< \yskip\hang|put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
---
> \yskip\hang|@!put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
9058c11342
< \yskip\hang|put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
---
> \yskip\hang|@!put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
9060c11344
< \yskip\hang|put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
---
> \yskip\hang|@!put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
9069,9071c11353,11355
< \yskip\hang|bop| 139 $c↓0[4]$ $c↓1[4]$ $\ldots$ $c↓9[4]$ $p[4]$. Beginning
< of a page: Set |(h,v,w,x,y,z)←(0,0,0,0,0,0)| and set the stack empty. Set
< the current font |f| to an undefined value. The ten $c↓i$ parameters hold
---
> \yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
> of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set
> the current font |f| to an undefined value. The ten $c_i$ parameters hold
9075c11359
< |p| points to the previous |bop| command in the file, where the first
---
> |p| points to the previous |bop| in the file; the first
9083c11367
< |h|@@coordinate; so it usually needs to be sorted into some order that is
---
> |h|~coordinate; so it usually needs to be sorted into some order that is
9095,9097c11379,11381
< \yskip\hang|right1| 143 |b[1]|. Set |h←h+b|, i.e., move right |b| units.
< The parameter is a signed number in two's complement notation, |-128≤b<128|;
< if |b<0|, the reference point actually moves left.
---
> \yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units.
> The parameter is a signed number in two's complement notation, |-128<=b<128|;
> if |b<0|, the reference point moves left.
9100c11384
< two-byte quantity in the range |-32768≤b<32768|.
---
> two-byte quantity in the range |-32768<=b<32768|.
9103c11387
< three-byte quantity in the range |@t$-2^{23}$@>≤b<@t$2^{23}$@>|.
---
> three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
9106c11390
< four-byte quantity in the range |@t$-2^{31}$@>≤b<@t$2^{31}$@>|.
---
> four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
9108c11392
< \yskip\hang|w0| 147. Set |h←h+w|; i.e., move right |w| units. With luck,
---
> \yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck,
9113,9115c11397,11399
< \yskip\hang|w1| 148 |b[1]|. Set |w←b| and |h←h+b|. The value of |b| is a
< signed quantity in two's complement notation, |-128≤b<128|. This command
< changes the current |w|@@spacing and moves right by |b|.
---
> \yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a
> signed quantity in two's complement notation, |-128<=b<128|. This command
> changes the current |w|~spacing and moves right by |b|.
9117,9118c11401,11402
< \yskip\hang|w2| 149 |b[2]|. Same as |w1|, but |b| is two bytes long,
< |-32768≤b<32768|.
---
> \yskip\hang|@!w2| 149 |b[2]|. Same as |w1|, but |b| is two bytes long,
> |-32768<=b<32768|.
9120,9121c11404,11405
< \yskip\hang|w3| 150 |b[3]|. Same as |w1|, but |b| is three bytes long,
< |@t$-2^{23}$@>≤b<@t$2^{23}$@>|.
---
> \yskip\hang|@!w3| 150 |b[3]|. Same as |w1|, but |b| is three bytes long,
> |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
9123,9124c11407,11408
< \yskip\hang|w4| 151 |b[4]|. Same as |w1|, but |b| is four bytes long,
< |@t$-2^{31}$@>≤b<@t$2^{31}$@>|.
---
> \yskip\hang|@!w4| 151 |b[4]|. Same as |w1|, but |b| is four bytes long,
> |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
9126c11410
< \yskip\hang|x0| 152. Set |h←h+x|; i.e., move right |x| units. The `|x|'
---
> \yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|'
9130,9132c11414,11416
< \yskip\hang|x1| 153 |b[1]|. Set |x←b| and |h←h+b|. The value of |b| is a
< signed quantity in two's complement notation, |-128≤b<128|. This command
< changes the current |x|@@spacing and moves right by |b|.
---
> \yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a
> signed quantity in two's complement notation, |-128<=b<128|. This command
> changes the current |x|~spacing and moves right by |b|.
9134,9135c11418,11419
< \yskip\hang|x2| 154 |b[2]|. Same as |x1|, but |b| is two bytes long,
< |-32768≤b<32768|.
---
> \yskip\hang|@!x2| 154 |b[2]|. Same as |x1|, but |b| is two bytes long,
> |-32768<=b<32768|.
9137,9138c11421,11422
< \yskip\hang|x3| 155 |b[3]|. Same as |x1|, but |b| is three bytes long,
< |@t$-2^{23}$@>≤b<@t$2^{23}$@>|.
---
> \yskip\hang|@!x3| 155 |b[3]|. Same as |x1|, but |b| is three bytes long,
> |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
9140,9141c11424,11425
< \yskip\hang|x4| 156 |b[4]|. Same as |x1|, but |b| is four bytes long,
< |@t$-2^{31}$@>≤b<@t$2^{31}$@>|.
---
> \yskip\hang|@!x4| 156 |b[4]|. Same as |x1|, but |b| is four bytes long,
> |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
9143,9145c11427,11429
< \yskip\hang|down1| 157 |a[1]|. Set |v←v+a|, i.e., move down |a| units.
< The parameter is a signed number in two's complement notation, |-128≤a<128|;
< if |a<0|, the reference point actually moves up.
---
> \yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units.
> The parameter is a signed number in two's complement notation, |-128<=a<128|;
> if |a<0|, the reference point moves up.
9147,9148c11431,11432
< \yskip\hang|down2| 158 |a[2]|. Same as |down1|, except that |a| is a
< two-byte quantity in the range |-32768≤a<32768|.
---
> \yskip\hang|@!down2| 158 |a[2]|. Same as |down1|, except that |a| is a
> two-byte quantity in the range |-32768<=a<32768|.
9150,9151c11434,11435
< \yskip\hang|down3| 159 |a[3]|. Same as |down1|, except that |a| is a
< three-byte quantity in the range |@t$-2^{23}$@>≤a<@t$2^{23}$@>|.
---
> \yskip\hang|@!down3| 159 |a[3]|. Same as |down1|, except that |a| is a
> three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
9153,9154c11437,11438
< \yskip\hang|down4| 160 |a[4]|. Same as |down1|, except that |a| is a
< four-byte quantity in the range |@t$-2^{31}$@>≤a<@t$2^{31}$@>|.
---
> \yskip\hang|@!down4| 160 |a[4]|. Same as |down1|, except that |a| is a
> four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
9156c11440
< \yskip\hang|y0| 161. Set |v←v+y|; i.e., move down |y| units. With luck,
---
> \yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck,
9161,9163c11445,11447
< \yskip\hang|y1| 162 |a[1]|. Set |y←a| and |v←v+a|. The value of |a| is a
< signed quantity in two's complement notation, |-128≤a<128|. This command
< changes the current |y|@@spacing and moves down by |a|.
---
> \yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a
> signed quantity in two's complement notation, |-128<=a<128|. This command
> changes the current |y|~spacing and moves down by |a|.
9165,9166c11449,11450
< \yskip\hang|y2| 163 |a[2]|. Same as |y1|, but |a| is two bytes long,
< |-32768≤a<32768|.
---
> \yskip\hang|@!y2| 163 |a[2]|. Same as |y1|, but |a| is two bytes long,
> |-32768<=a<32768|.
9168,9169c11452,11453
< \yskip\hang|y3| 164 |a[3]|. Same as |y1|, but |a| is three bytes long,
< |@t$-2^{23}$@>≤a<@t$2^{23}$@>|.
---
> \yskip\hang|@!y3| 164 |a[3]|. Same as |y1|, but |a| is three bytes long,
> |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
9171,9172c11455,11456
< \yskip\hang|y4| 165 |a[4]|. Same as |y1|, but |a| is four bytes long,
< |@t$-2^{31}$@>≤a<@t$2^{31}$@>|.
---
> \yskip\hang|@!y4| 165 |a[4]|. Same as |y1|, but |a| is four bytes long,
> |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
9174c11458
< \yskip\hang|z0| 166. Set |v←v+z|; i.e., move down |z| units. The `|z|' commands
---
> \yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands
9177,9179c11461,11463
< \yskip\hang|z1| 167 |a[1]|. Set |z←a| and |v←v+a|. The value of |a| is a
< signed quantity in two's complement notation, |-128≤a<128|. This command
< changes the current |z|@@spacing and moves down by |a|.
---
> \yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a
> signed quantity in two's complement notation, |-128<=a<128|. This command
> changes the current |z|~spacing and moves down by |a|.
9181,9182c11465,11466
< \yskip\hang|z2| 168 |a[2]|. Same as |z1|, but |a| is two bytes long,
< |-32768≤a<32768|.
---
> \yskip\hang|@!z2| 168 |a[2]|. Same as |z1|, but |a| is two bytes long,
> |-32768<=a<32768|.
9184,9185c11468,11469
< \yskip\hang|z3| 169 |a[3]|. Same as |z1|, but |a| is three bytes long,
< |@t$-2^{23}$@>≤a<@t$2^{23}$@>|.
---
> \yskip\hang|@!z3| 169 |a[3]|. Same as |z1|, but |a| is three bytes long,
> |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
9187,9188c11471,11472
< \yskip\hang|z4| 170 |a[4]|. Same as |z1|, but |a| is four bytes long,
< |@t$-2^{31}$@>≤a<@t$2^{31}$@>|.
---
> \yskip\hang|@!z4| 170 |a[4]|. Same as |z1|, but |a| is four bytes long,
> |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
9190c11474,11475
< \yskip\hang|fnt_num_0| 171. Set |f←0|.
---
> \yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been
> defined by a \\{fnt\_def} instruction, as explained below.
9192,9193c11477,11478
< \yskip\hang|fnt_num_1| through |fnt_num_63| (opcodes 172 to 234). Set
< |f←1|, $\ldotss$, \hbox{|f←63|}, respectively.
---
> \yskip\hang\\{fnt\_num\_1} through \\{fnt\_num\_63} (opcodes 172 to 234). Set
> |f:=1|, \dots, \hbox{|f:=63|}, respectively.
9195,9196c11480,11481
< \yskip\hang|fnt1| 235 |n[1]|. Set |f←n|. \TeX82 uses this command for font
< numbers in the range |64≤n<256|.
---
> \yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font
> numbers in the range |64<=k<256|.
9198,9199c11483,11484
< \yskip\hang|fnt2| 236 |n[2]|. Same as |fnt1|, except that@@|n| is two
< bytes long, so it is in the range |0≤n<65536|. \TeX82 never generates this
---
> \yskip\hang|@!fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
> bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this
9204c11489
< \yskip\hang|fnt3| 237 |n[3]|. Same as |fnt1|, except that@@|n| is three
---
> \yskip\hang|@!fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
9207,9217c11492,11513
< \yskip\hang|fnt4| 238 |n[4]|. Same as |fnt1|, except that@@|n| is four
< bytes long; this is for the really big font numbers. The value $-1$
< is forbidden, so the legal values of@@|f| are $-2^{31}\L f<-1$ and
< $0\L f<2^{31}-1$.
<
< \yskip\hang|xxx| 239 |m[1]| |x[m]|. This command is undefined in
< general; it functions as an $(m+2)$-byte |nop| unless special \.{DVI}-reading
< programs are being used. \TeX82 generates this command when an \.{\\xsend}
< appears, setting |m| to the number of bytes being sent. It is recommended that
< |x| be a string having the form of a keyword followed by possible parameters
< relevant to that keyword.
---
> \yskip\hang|@!fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
> bytes long; this is for the really big font numbers (and for the negative ones).
>
> \yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
> general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
> programs are being used. \TeX82 generates |xxx1| when a short enough
> \.{\\special} appears, setting |k| to the number of bytes being sent. It
> is recommended that |x| be a string having the form of a keyword followed
> by possible parameters relevant to that keyword.
>
> \yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
>
> \yskip\hang|@!xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
>
> \yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
> large. \TeX82 uses |xxx4| when sending a string of length 256 or more.
>
> \yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
> Define font |k|, where |0<=k<256|; font definitions will be explained shortly.
>
> \yskip\hang|@!fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
> Define font |k|, where |0<=k<65536|.
9219c11515,11516
< \yskip\hang|pst| 240. Beginning of the postamble, see below.
---
> \yskip\hang|@!fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
> Define font |k|, where |0<=k<@t$2^{24}$@>|.
9221c11518,11529
< \yskip\noindent Commands 241--255 are undefined at the present time.
---
> \yskip\hang|@!fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
> Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
>
> \yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
> Beginning of the preamble; this must come at the very beginning of the
> file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.
>
> \yskip\hang|post| 248. Beginning of the postamble, see below.
>
> \yskip\hang|post_post| 249. Ending of the postamble, see below.
>
> \yskip\noindent Commands 250--255 are undefined at the present time.
9226d11533
< @d put1=133 {typeset a character}
9245,9246c11552,11619
< @d xxx=239 {extension to \.{DVI} primitives}
< @d pst=240 {postamble}
---
> @d xxx1=239 {extension to \.{DVI} primitives}
> @d xxx4=242 {potentially long extension to \.{DVI} primitives}
> @d fnt_def1=243 {define the meaning of a font number}
> @d pre=247 {preamble}
> @d post=248 {postamble beginning}
> @d post_post=249 {postamble ending}
>
> @ The preamble contains basic information about the file as a whole. As
> stated above, there are six parameters:
> $$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
> The |i| byte identifies \.{DVI} format; currently this byte is always set
> to~2. (The value |i=3| is currently used for an extended format that
> allows a mixture of right-to-left and left-to-right typesetting.
> Some day we will set |i=4|, when \.{DVI} format makes another
> incompatible change---perhaps in the year 2048.)
>
> The next two parameters, |num| and |den|, are positive integers that define
> the units of measurement; they are the numerator and denominator of a
> fraction by which all dimensions in the \.{DVI} file could be multiplied
> in order to get lengths in units of $10^{-7}$ meters. Since $\rm 7227{pt} =
> 254{cm}$, and since \TeX\ works with scaled points where there are $2^{16}$
> sp in a point, \TeX\ sets
> $|num|/|den|=(254\cdot10^5)/(7227\cdot2^{16})=25400000/473628672$.
> @^sp@>
>
> The |mag| parameter is what \TeX\ calls \.{\\mag}, i.e., 1000 times the
> desired magnification. The actual fraction by which dimensions are
> multiplied is therefore $|mag|\cdot|num|/1000|den|$. Note that if a \TeX\
> source document does not call for any `\.{true}' dimensions, and if you
> change it only by specifying a different \.{\\mag} setting, the \.{DVI}
> file that \TeX\ creates will be completely unchanged except for the value
> of |mag| in the preamble and postamble. (Fancy \.{DVI}-reading programs allow
> users to override the |mag|~setting when a \.{DVI} file is being printed.)
>
> Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
> interpreted further. The length of comment |x| is |k|, where |0<=k<256|.
>
> @d id_byte=2 {identifies the kind of \.{DVI} files described here}
>
> @ Font definitions for a given font number |k| contain further parameters
> $$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
> The four-byte value |c| is the check sum that \TeX\ found in the \.{TFM}
> file for this font; |c| should match the check sum of the font found by
> programs that read this \.{DVI} file.
> @^check sum@>
>
> Parameter |s| contains a fixed-point scale factor that is applied to
> the character widths in font |k|; font dimensions in \.{TFM} files and
> other font files are relative to this quantity, which is called the
> ``at size'' elsewhere in this documentation. The value of |s| is
> always positive and less than $2^{27}$. It is given in the same units
> as the other \.{DVI} dimensions, i.e., in sp when \TeX82 has made the
> file. Parameter |d| is similar to |s|; it is the ``design size,'' and
> (like~|s|) it is given in \.{DVI} units. Thus, font |k| is to be used
> at $|mag|\cdot s/1000d$ times its normal size.
>
> The remaining part of a font definition gives the external name of the font,
> which is an ASCII string of length |a+l|. The number |a| is the length
> of the ``area'' or directory, and |l| is the length of the font name itself;
> the standard local system font area is supposed to be used when |a=0|.
> The |n| field contains the area in its first |a| bytes.
>
> Font definitions must appear before the first use of a particular font number.
> Once font |k| is defined, it must not be defined again; however, we
> shall see below that font definitions appear in the postamble as well as
> in the pages, so in this sense each font number is defined exactly twice,
> if at all. Like |nop| commands, font definitions can
> appear before the first |bop|, or between an |eop| and a |bop|.
9251c11624
< generated by \MF\ by adhering to the following principles: (1)@@The \MF\
---
> generated by \MF\ by adhering to the following principles: (1)~The \MF\
9254c11627
< reference point, i.e., in row@@0 and column@@0 of the \MF\ raster. This
---
> reference point, i.e., in row~0 and column~0 of the \MF\ raster. This
9257c11630
< the \.{DVI} file. (2)@@A typeset rule of height $a>0$ and width $b>0$
---
> the \.{DVI} file. (2)~A typeset rule of height $a>0$ and width $b>0$
9260c11633
< |0≤x<@t$\alpha$@>b| and |0≤y<@t$\alpha$@>a|, where $\alpha$ is the number
---
> |0<=x<@t$\alpha$@>b| and |0<=y<@t$\alpha$@>a|, where $\alpha$ is the number
9266c11639
< @ The last page in a \.{DVI} file is followed by `|pst|'; this command
---
> @ The last page in a \.{DVI} file is followed by `|post|'; this command
9268,9288c11641,11649
< accumulated about the file. The postamble has the form
< $$\hbox{|p[4]| |n[4]| |d[4]| |m[4]| |l[4]| |u[4]| |s[2]| |t[2]|
< $\langle\,$font definitions$\,\rangle$
< $(-1)[4]$ |q[4]| |i[1]| 223's|[≥4]|}$$
< Here |p| is a pointer to the final |bop| in the file. The next two parameters,
< |n| and |d|, are positive integers that define the units of measurement;
< they are the numerator and denominator of a fraction by which all dimensions
< in the \.{DVI} file could be multiplied in order to get lengths in units
< of $10^{-7}$ meters. (Since there are 72.27 points per inch and 2.54 centimeters
< per inch, and since \TeX82 works with scaled points where there are $2^{16}$
< sp in a point, \TeX82 sets |n=25400000| and $d=7227\cdot2^{16}=473628672$.)
< @^sp@>
<
< The next parameter, |m|, is \TeX's \.{\\mag} parameter, i.e., 1000 times the
< desired magnification. The actual fraction by which dimensions are multiplied
< is therefore $mn/1000d$. Note that if a \TeX\ source document does not call
< for any `\.{true}' dimensions, and if you change it only by specifying a
< different \.{\\mag} setting, the \.{DVI} file that \TeX\ creates will be
< completely unchanged except for the value of |m| in the postamble. (Fancy
< \.{DVI}-reading programs allow users to override the |m|@@setting when a
< \.{DVI} file is being printed.)
---
> accumulated about the file, making it possible to print subsets of the data
> with reasonable efficiency. The postamble has the form
> $$\vbox{\halign{\hbox{#\hfil}\cr
> |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
> $\langle\,$font definitions$\,\rangle$\cr
> |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
> Here |p| is a pointer to the final |bop| in the file. The next three
> parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
> appeared in the preamble.
9293,9296c11654,11664
< position individual ``pages'' on large sheets of film or paper.
< Parameter |s| is the maximum stack depth (i.e., the excess of |push| commands
< over |pop| commands) needed to process this file. Then comes |t|, the total
< number of pages (|bop| commands) present.
---
> position individual ``pages'' on large sheets of film or paper; however,
> the standard convention for output on normal size paper is to position each
> page so that the upper left-hand corner is exactly one inch from the left
> and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer
> software that attempts cleverly to center the output; a fixed position of
> the upper left corner is easiest for users to understand and to work with.
> Therefore |l| and~|u| are often ignored.
>
> Parameter |s| is the maximum stack depth (i.e., the largest excess of
> |push| commands over |pop| commands) needed to process this file. Then
> comes |t|, the total number of pages (|bop| commands) present.
9299,9327c11667,11675
< speci\-fi\-ca\-tions having the form
< \def\f{↓{\!f}}
< $$\hbox{|f[4]| $c\f[4]$ $s\f[4]$ $a\f[1]$ $l\f[1]$ $n\f[a\f+l\f]$.}$$
< The first parameter in a font definition is the font number, $f$; this
< must be different from $-1$ and distinct from the font numbers in other
< definitions. (Note that the font definitions are followed by the four-byte
< value $-1$, so it will be clear when the definitions have ended.) The
< next parameter, $c\f$, is the check sum that \TeX\ found in the \.{TFM}
< file for this font; it should match the check sum of the font found by
< @^check sum@>
< programs that read this \.{DVI} file.
< Parameter $s\f$ contains a fixed-point scale factor that is applied to the
< character widths in font |f|; font dimensions in \.{TFM} files and other font
< files are relative to this quantity, which is called the ``at size'' elsewhere
< in this documentation. The value of $s\f$ is always positive and less than
< $2^{27}$. It is given in the same units as the other dimensions of the
< file, i.e., in sp.
<
< The remaining part of a font definition gives the external name of the font,
< which is an ascii string of length $a\f+l\f$. The number $a\f$ is the length
< of the ``area'' or directory, and $l\f$ is the length of the font name itself;
< the standard local system font area is supposed to be used when $a\f=0$.
< The $n\f$ field contains the area in its first $a\f$ bytes.
<
< @ The last part of the postamble, following the phony font number
< $-1$, contains |q|, a pointer to the |pst| command that started the
< postamble. An identification byte, |i|, comes next; currently this byte
< is always set to@@2. (Some day we will set |i=3|, when \.{DVI} format
< makes another incompatible change---perhaps in 1992.)
---
> \\{fnt\_def} commands as described above, possibly interspersed with |nop|
> commands. Each font number that is used in the \.{DVI} file must be defined
> exactly twice: Once before it is first selected by a \\{fnt} command, and once
> in the postamble.
>
> @ The last part of the postamble, following the |post_post| byte that
> signifies the end of the font definitions, contains |q|, a pointer to the
> |post| command that started the postamble. An identification byte, |i|,
> comes next; this currently equals~2, as in the preamble.
9329c11677
< Following the |i| byte there are four or more bytes that are all equal to
---
> The |i| byte is followed by four or more bytes that are all equal to
9341,9345c11689,11693
< the \.{DVI} reader starts at the end and skips backwards over the 223's
< until finding the identification byte. Then it backs up four bytes, reads
< |q|, and goes to byte |q| of the file. This byte should, of course,
< contain the value 240 (|pst|); now the postamble can be read, so the
< \.{DVI} reader discovers all the information needed for typesetting the
---
> the \.{DVI} reader can start at the end and skip backwards over the 223's
> until finding the identification byte. Then it can back up four bytes, read
> |q|, and move to byte |q| of the file. This byte should, of course,
> contain the value 248 (|post|); now the postamble can be read, so the
> \.{DVI} reader can discover all the information needed for typesetting the
9348,9362c11696,11697
< desirable.
<
< The reason for reading the postamble first is that the \.{DVI} reader must
< know the widths of characters, in order to find out where things go on a page;
< and it needs to know the names of the fonts, so that it can get their widths
< from a \.{TFM} file or from some other kind of font-information file.
< The reason for writing the postamble last is that \TeX\ can't put out all
< the font names until it has finished generating the pages of the \.{DVI}
< file, since new fonts can occur anywhere in a \TeX\ job; and the alternative
< of sprinkling font definitions throughout a \.{DVI} file is unattractive,
< since that would make it necessary to read the whole file even when
< printing only one page. Furthermore, we wouldn't want to copy the
< information in the first part of a \.{DVI} file to the end of another file
< that begins with the postamble information, since the first part
< is typically quite long.
---
> desirable. This saves a lot of time, since \.{DVI} files used in production
> jobs tend to be large.
9370,9371c11705,11708
< \PASCAL, one can simply read them twice, first skipping to the postamble
< and then doing the pages.
---
> \PASCAL, one can simply read them from front to back, since the necessary
> header information is present in the preamble and in the font definitions.
> (The |l| and |u| and |s| and |t| parameters, which appear only in the
> postamble, are ``frills'' that are handy but not absolutely necessary.)
9373,9374c11710
< @d id_byte=2 {identifies the kind of \.{DVI} files described here}
< @* \[31] Shipping pages out.
---
> @* \[32] Shipping pages out.
9393a11730,11731
> The variable |dead_cycles| contains the number of times an output routine
> has been initiated since the last |ship_out|.
9406a11745
> @!dead_cycles:integer; {recent outputs that didn't ship anything out}
9410d11748
< @!hd:quarterword; {height and depth indices for a character}
9413c11751
< @!lq,@!lr,@!lx:integer; {quantities used in calculations for leaders}
---
> @!lq,@!lr:integer; {quantities used in calculations for leaders}
9416,9417c11754,11755
< total_pages←0; max_v←0; max_h←0; max_push←0; last_bop←-1;
< doing_leaders←false;
---
> total_pages:=0; max_v:=0; max_h:=0; max_push:=0; last_bop:=-1;
> doing_leaders:=false; dead_cycles:=0; cur_s:=-1;
9440c11778
< A byte is present in the buffer only if its number is |≥dvi_gone|.
---
> A byte is present in the buffer only if its number is |>=dvi_gone|.
9446a11785
> @^system dependencies@>
9454c11793
< output buffer has been fully emptied}
---
> output buffer has been fully emptied}
9461,9462c11800,11801
< half_buf←dvi_buf_size div 2; dvi_limit←dvi_buf_size; dvi_ptr←0;
< dvi_offset←0; dvi_gone←0;
---
> half_buf:=dvi_buf_size div 2; dvi_limit:=dvi_buf_size; dvi_ptr:=0;
> dvi_offset:=0; dvi_gone:=0;
9477c11816
< begin for k←a to b do write(dvi_file,dvi_buf[k]);
---
> begin for k:=a to b do write(dvi_file,dvi_buf[k]);
9483,9485c11822,11824
< @d dvi_out(#)==@+begin dvi_buf[dvi_ptr]←#; incr(dvi_ptr);
< if dvi_ptr=dvi_limit then dvi_swap;
< end
---
> @d dvi_out(#)==@+begin dvi_buf[dvi_ptr]:=#; incr(dvi_ptr);
> if dvi_ptr=dvi_limit then dvi_swap;
> end
9489,9494c11828,11833
< begin write_dvi(0,half_buf-1); dvi_limit←half_buf;
< dvi_offset←dvi_offset+dvi_buf_size; dvi_ptr←0;
< end
< else begin write_dvi(half_buf,dvi_buf_size-1); dvi_limit←dvi_buf_size;
< end;
< dvi_gone←dvi_gone+half_buf;
---
> begin write_dvi(0,half_buf-1); dvi_limit:=half_buf;
> dvi_offset:=dvi_offset+dvi_buf_size; dvi_ptr:=0;
> end
> else begin write_dvi(half_buf,dvi_buf_size-1); dvi_limit:=dvi_buf_size;
> end;
> dvi_gone:=dvi_gone+half_buf;
9498c11837
< will be a multiple of@@4.
---
> will be a multiple of~4.
9508,9514c11847,11853
< begin if x≥0 then dvi_out(x div @'100000000)
< else begin x:=x+@'10000000000;
< x:=x+@'10000000000;
< dvi_out((x div @'100000000) + 128);
< end;
< x←x mod @'100000000; dvi_out(x div @'200000);
< x←x mod @'200000; dvi_out(x div @'400);
---
> begin if x>=0 then dvi_out(x div @'100000000)
> else begin x:=x+@'10000000000;
> x:=x+@'10000000000;
> dvi_out((x div @'100000000) + 128);
> end;
> x:=x mod @'100000000; dvi_out(x div @'200000);
> x:=x mod @'200000; dvi_out(x div @'400);
9517a11857,11889
> @ A mild optimization of the output is performed by the |dvi_pop|
> routine, which issues a |pop| unless it is possible to cancel a
> `|push| |pop|' pair. The parameter to |dvi_pop| is the byte address
> following the old |push| that matches the new |pop|.
>
> @p procedure dvi_pop(@!l:integer);
> begin if (l=dvi_offset+dvi_ptr)and(dvi_ptr>0) then decr(dvi_ptr)
> else dvi_out(pop);
> end;
>
> @ Here's a procedure that outputs a font definition. Since \TeX82 uses at
> most 256 different fonts per job, |fnt_def1| is always used as the command code.
>
> @p procedure dvi_font_def(@!f:internal_font_number);
> var k:pool_pointer; {index into |str_pool|}
> begin dvi_out(fnt_def1);
> dvi_out(f-font_base-1);@/
> dvi_out(qo(font_check[f].b0));
> dvi_out(qo(font_check[f].b1));
> dvi_out(qo(font_check[f].b2));
> dvi_out(qo(font_check[f].b3));@/
> dvi_four(font_size[f]);
> dvi_four(font_dsize[f]);@/
> dvi_out(length(font_area[f]));
> dvi_out(length(font_name[f]));
> @<Output the font name whose internal number is |f|@>;
> end;
>
> @ @<Output the font name whose internal number is |f|@>=
> for k:=str_start[font_area[f]] to str_start[font_area[f]+1]-1 do
> dvi_out(so(str_pool[k]));
> for k:=str_start[font_name[f]] to str_start[font_name[f]+1]-1 do
> dvi_out(so(str_pool[k]))
9536,9539c11908,11911
< $$3↓z\,1↓y\,4↓d\,1↓y\,5↓y\,9↓d\,2↓d\,6↓d\,5↓y\,3↓z\,5↓y\,8↓d\,9↓d.$$
< There are three $y$-hits ($1↓y\ldotsm1↓y$ and $5↓y\ldotsm5↓y\ldotsm5↓y$) and
< one $z$-hit ($3↓z\ldotsm3↓z$); there are no $d$-hits, since the two appearances
< of $9↓d$ have $d$'s between them, but we don't count $d$-hits so it doesn't
---
> $$3_z\,1_y\,4_d\,1_y\,5_y\,9_d\,2_d\,6_d\,5_y\,3_z\,5_y\,8_d\,9_d.$$
> There are three $y$-hits ($1_y\ldots1_y$ and $5_y\ldots5_y\ldots5_y$) and
> one $z$-hit ($3_z\ldots3_z$); there are no $d$-hits, since the two appearances
> of $9_d$ have $d$'s between them, but we don't count $d$-hits so it doesn't
9547,9548c11919,11920
< sequence until one of the following things happens: (a)@@You see
< $\delta↓y$ or $\delta↓z$, and this was the first time you encountered a
---
> sequence until one of the following things happens: (a)~You see
> $\delta_y$ or $\delta_z$, and this was the first time you encountered a
9550c11922
< $\delta$; you have scored a hit. (b)@@You see $\delta↓d$, and no $y$
---
> $\delta$; you have scored a hit. (b)~You see $\delta_d$, and no $y$
9552c11924
< the previous $\delta↓d$ to $\delta↓y$ (this corresponds to changing a
---
> the previous $\delta_d$ to $\delta_y$ (this corresponds to changing a
9554,9556c11926,11928
< another hit. (c)@@You see $\delta↓d$, and a $y$ subscript has been seen
< but not a $z$. Change the previous $\delta↓d$ to $\delta↓z$ and assign
< $z$ to the new $\delta$. (d)@@You encounter both $y$ and $z$ subscripts
---
> another hit. (c)~You see $\delta_d$, and a $y$ subscript has been seen
> but not a $z$. Change the previous $\delta_d$ to $\delta_z$ and assign
> $z$ to the new $\delta$. (d)~You encounter both $y$ and $z$ subscripts
9561c11933
< The subscripts $3↓z\,1↓y\,4↓d\ldotsm$ in the example above were, in fact,
---
> The subscripts $3_z\,1_y\,4_d\ldots\,$ in the example above were, in fact,
9584c11956
< down_ptr←null; right_ptr←null;
---
> down_ptr:=null; right_ptr:=null;
9597,9598c11969,11970
< begin q←get_node(movement_node_size); {new node for the top of the stack}
< width(q)←w; location(q)←dvi_offset+dvi_ptr;
---
> begin q:=get_node(movement_node_size); {new node for the top of the stack}
> width(q):=w; location(q):=dvi_offset+dvi_ptr;
9600,9603c11972,11975
< begin link(q)←down_ptr; down_ptr←q;
< end
< else begin link(q)←right_ptr; right_ptr←q;
< end;
---
> begin link(q):=down_ptr; down_ptr:=q;
> end
> else begin link(q):=right_ptr; right_ptr:=q;
> end;
9605c11977
< to generate; |goto found| if node |p| is a ``hit''@>;
---
> to generate; |goto found| if node |p| is a ``hit''@>;
9608c11980
< appearance of@@|w|@>;
---
> appearance of~|w|@>;
9625c11997
< redefining hits so that $\delta↓y\ldotsm \delta↓y$ is a hit if all $y$'s between
---
> redefining hits so that $\delta_y\ldots \delta_y$ is a hit if all $y$'s between
9627c11999
< parenthesis level of the right-hand $\delta↓y$ is deeper than or equal to
---
> parenthesis level of the right-hand $\delta_y$ is deeper than or equal to
9631,9634c12003,12009
< $$2↓y\,7↓d\,1↓d\,(\,8↓z\,2↓y\,8↓z\,)\,1$$
< we cannot change the previous $1↓d$ to $1↓y$, since that would invalidate
< the $2↓y\ldotsm2↓y$ hit. But we can change it to $1↓z$, scoring a hit
< since the intervening $8↓z$'s are enclosed in parentheses.
---
> $$2_y\,7_d\,1_d\,(\,8_z\,2_y\,8_z\,)\,1$$
> we cannot change the previous $1_d$ to $1_y$, since that would invalidate
> the $2_y\ldots2_y$ hit. But we can change it to $1_z$, scoring a hit
> since the intervening $8_z$'s are enclosed in parentheses.
>
> The program below removes movement nodes that are introduced after a |push|,
> before it outputs the corresponding |pop|.
9649c12024
< info(q)←info(p);
---
> info(q):=info(p);
9651,9670c12026,12045
< begin dvi_out(o+y0-down1); {|y0| or |w0|}
< while link(q)≠p do
< begin q←link(q);
< case info(q) of
< yz_OK: info(q)←z_OK;
< y_OK: info(q)←d_fixed;
< othercases do_nothing
< endcases;
< end;
< end
< else begin dvi_out(o+z0-down1); {|z0| or |x0|}
< while link(q)≠p do
< begin q←link(q);
< case info(q) of
< yz_OK: info(q)←y_OK;
< z_OK: info(q)←d_fixed;
< othercases do_nothing
< endcases;
< end;
< end
---
> begin dvi_out(o+y0-down1); {|y0| or |w0|}
> while link(q)<>p do
> begin q:=link(q);
> case info(q) of
> yz_OK: info(q):=z_OK;
> y_OK: info(q):=d_fixed;
> othercases do_nothing
> endcases;
> end;
> end
> else begin dvi_out(o+z0-down1); {|z0| or |x0|}
> while link(q)<>p do
> begin q:=link(q);
> case info(q) of
> yz_OK: info(q):=y_OK;
> z_OK: info(q):=d_fixed;
> othercases do_nothing
> endcases;
> end;
> end
9673,9687c12048,12062
< info(q)←yz_OK;
< if abs(w)≥@'40000000 then
< begin dvi_out(o+3); {|down4| or |right4|}
< dvi_four(w); return;
< end;
< if abs(w)≥@'100000 then
< begin dvi_out(o+2); {|down3| or |right3|}
< if w<0 then w←w+@'100000000;
< dvi_out(w div @'200000); w←w mod @'200000; goto 2;
< end;
< if abs(w)≥@'200 then
< begin dvi_out(o+1); {|down2| or |right2|}
< if w<0 then w←w+@'200000;
< goto 2;
< end;
---
> info(q):=yz_OK;
> if abs(w)>=@'40000000 then
> begin dvi_out(o+3); {|down4| or |right4|}
> dvi_four(w); return;
> end;
> if abs(w)>=@'100000 then
> begin dvi_out(o+2); {|down3| or |right3|}
> if w<0 then w:=w+@'100000000;
> dvi_out(w div @'200000); w:=w mod @'200000; goto 2;
> end;
> if abs(w)>=@'200 then
> begin dvi_out(o+1); {|down2| or |right2|}
> if w<0 then w:=w+@'200000;
> goto 2;
> end;
9689c12064
< if w<0 then w←w+@'400;
---
> if w<0 then w:=w+@'400;
9698a12074
> @^inner loop@>
9705,9716c12081,12092
< p←link(q); mstate←none_seen;
< while p≠null do
< begin if width(p)=w then @<Consider a node with matching width;
< |goto found| if it's a hit@>
< else case mstate+info(p) of
< none_seen+y_here: mstate←y_seen;
< none_seen+z_here: mstate←z_seen;
< y_seen+z_here,z_seen+y_here: goto not_found;
< othercases do_nothing
< endcases;
< p←link(p);
< end;
---
> p:=link(q); mstate:=none_seen;
> while p<>null do
> begin if width(p)=w then @<Consider a node with matching width;
> |goto found| if it's a hit@>
> else case mstate+info(p) of
> none_seen+y_here: mstate:=y_seen;
> none_seen+z_here: mstate:=z_seen;
> y_seen+z_here,z_seen+y_here: goto not_found;
> othercases do_nothing
> endcases;
> p:=link(p);
> end;
9721c12097
< moving finger writes, $\ldotss\,$.''
---
> moving finger writes, $\ldots\,\,$.''
9725,9730c12101,12106
< none_seen+yz_OK,none_seen+y_OK,z_seen+yz_OK,z_seen+y_OK:@;@/
< if location(p)<dvi_gone then goto not_found
< else @<Change buffered instruction to |y| or |w| and |goto found|@>;
< none_seen+z_OK,y_seen+yz_OK,y_seen+z_OK:@;@/
< if location(p)<dvi_gone then goto not_found
< else @<Change buffered instruction to |z| or |x| and |goto found|@>;
---
> none_seen+yz_OK,none_seen+y_OK,z_seen+yz_OK,z_seen+y_OK:@t@>@;@/
> if location(p)<dvi_gone then goto not_found
> else @<Change buffered instruction to |y| or |w| and |goto found|@>;
> none_seen+z_OK,y_seen+yz_OK,y_seen+z_OK:@t@>@;@/
> if location(p)<dvi_gone then goto not_found
> else @<Change buffered instruction to |z| or |x| and |goto found|@>;
9736,9739c12112,12115
< begin k←location(p)-dvi_offset;
< if k<0 then k←k+dvi_buf_size;
< dvi_buf[k]←dvi_buf[k]+y1-down1;
< info(p)←y_here; goto found;
---
> begin k:=location(p)-dvi_offset;
> if k<0 then k:=k+dvi_buf_size;
> dvi_buf[k]:=dvi_buf[k]+y1-down1;
> info(p):=y_here; goto found;
9743,9746c12119,12122
< begin k←location(p)-dvi_offset;
< if k<0 then k←k+dvi_buf_size;
< dvi_buf[k]←dvi_buf[k]+z1-down1;
< info(p)←z_here; goto found;
---
> begin k:=location(p)-dvi_offset;
> if k<0 then k:=k+dvi_buf_size;
> dvi_buf[k]:=dvi_buf[k]+z1-down1;
> info(p):=z_here; goto found;
9756c12132
< {delete movement nodes with |location≥l|}
---
> {delete movement nodes with |location>=l|}
9759,9766c12135,12142
< begin while down_ptr≠null do
< begin if location(down_ptr)<l then goto done;
< p←down_ptr; down_ptr←link(p); free_node(p,movement_node_size);
< end;
< done: while right_ptr≠null do
< begin if location(right_ptr)<l then return;
< p←right_ptr; right_ptr←link(p); free_node(p,movement_node_size);
< end;
---
> begin while down_ptr<>null do
> begin if location(down_ptr)<l then goto done;
> p:=down_ptr; down_ptr:=link(p); free_node(p,movement_node_size);
> end;
> done: while right_ptr<>null do
> begin if location(right_ptr)<l then return;
> p:=right_ptr; right_ptr:=link(p); free_node(p,movement_node_size);
> end;
9777c12153
< Therefore \TeX\ maintains two pairs of global variables: |dvi_h| and |dvi_v|
---
> Therefore, \TeX\ maintains two pairs of global variables: |dvi_h| and |dvi_v|
9792,9797c12168,12173
< @d synch_h==if cur_h≠dvi_h then
< begin movement(cur_h-dvi_h,right1); dvi_h←cur_h;
< end
< @d synch_v==if cur_v≠dvi_v then
< begin movement(cur_v-dvi_v,down1); dvi_v←cur_v;
< end
---
> @d synch_h==if cur_h<>dvi_h then
> begin movement(cur_h-dvi_h,right1); dvi_h:=cur_h;
> end
> @d synch_v==if cur_v<>dvi_v then
> begin movement(cur_v-dvi_v,down1); dvi_v:=cur_v;
> end
9803c12179
< @!cur_s:integer; {current depth of output box nesting}
---
> @!cur_s:integer; {current depth of output box nesting, initially $-1$}
9806,9807c12182,12197
< dvi_h←0; dvi_v←0; cur_h←0; dvi_f←undefined_font;
< cur_s←-1; ensure_dvi_open;
---
> dvi_h:=0; dvi_v:=0; cur_h:=h_offset; dvi_f:=null_font;
> ensure_dvi_open;
> if total_pages=0 then
> begin dvi_out(pre); dvi_out(id_byte); {output the preamble}
> @^preamble of \.{DVI} file@>
> dvi_four(25400000); dvi_four(473628672); {conversion ratio for sp}
> prepare_mag; dvi_four(mag); {magnification factor is frozen}
> old_setting:=selector; selector:=new_string;
> print(" TeX output "); print_int(year); print_char(".");
> print_two(month); print_char("."); print_two(day);
> print_char(":"); print_two(time div 60);
> print_two(time mod 60);
> selector:=old_setting; dvi_out(cur_length);
> for s:=str_start[str_ptr] to pool_ptr-1 do dvi_out(so(str_pool[s]));
> pool_ptr:=str_start[str_ptr]; {flush the current string}
> end
9819c12209
< recursive}
---
> recursive}
9827c12217
< @d advance=13 {go to this label when advancing past glue or a rule}
---
> @d move_past=13 {go to this label when advancing past glue or a rule}
9833c12223
< label reswitch, advance, fin_rule, next_p;
---
> label reswitch, move_past, fin_rule, next_p;
9834a12225
> @!left_edge: scaled; {the left coordinate for this box}
9842a12234
> @!lx:scaled; {extra space between leader boxes}
9845,9846c12237,12242
< begin this_box←temp_ptr; g_order←glue_order(this_box);
< g_sign←glue_sign(this_box); p←list_ptr(this_box);
---
> @!glue_temp:real; {glue value before rounding}
> @!cur_glue:real; {glue seen so far}
> @!cur_g:scaled; {rounded equivalent of |cur_glue| times the glue ratio}
> begin cur_g:=0; cur_glue:=float_constant(0);
> this_box:=temp_ptr; g_order:=glue_order(this_box);
> g_sign:=glue_sign(this_box); p:=list_ptr(this_box);
9848,9852c12244,12251
< if cur_s>max_push then max_push←cur_s;
< save_loc←dvi_offset+dvi_ptr; base_line←cur_v;
< while p≠null do @<Output node |p| for |hlist_out| and move to the next node,
< maintaining the condition |cur_v=base_line|@>;
< prune_movements(save_loc); decr(cur_s);
---
> if cur_s>0 then dvi_out(push);
> if cur_s>max_push then max_push:=cur_s;
> save_loc:=dvi_offset+dvi_ptr; base_line:=cur_v; left_edge:=cur_h;
> while p<>null do @<Output node |p| for |hlist_out| and move to the next node,
> maintaining the condition |cur_v=base_line|@>;
> prune_movements(save_loc);
> if cur_s>0 then dvi_pop(save_loc);
> decr(cur_s);
9863,9875c12262,12273
< begin synch_h; synch_v;
< repeat f←font(p); c←character(p);
< if f≠dvi_f then @<Change font |dvi_f| to |f|@>;
< if c<128+min_quarterword then dvi_out(qo(c))
< else begin dvi_out(set1); dvi_out(qo(c));
< end;
< cur_h←cur_h+char_width(f)(char_info(f)(c));
< p←link(p);
< until not is_char_node(p);
< dvi_h←cur_h;
< end
< else if p≠null then @<Output the non-|char_node| |p| for |hlist_out|
< and move to the next node@>
---
> begin synch_h; synch_v;
> repeat f:=font(p); c:=character(p);
> if f<>dvi_f then @<Change font |dvi_f| to |f|@>;
> if c>=qi(128) then dvi_out(set1);
> dvi_out(qo(c));@/
> cur_h:=cur_h+char_width(f)(char_info(f)(c));
> p:=link(p);
> until not is_char_node(p);
> dvi_h:=cur_h;
> end
> else @<Output the non-|char_node| |p| for |hlist_out|
> and move to the next node@>
9878,9882c12276,12282
< begin if f≤64+font_base then dvi_out(f-font_base-1+fnt_num_0)
< else begin dvi_out(fnt1); dvi_out(f-font_base-1);
< end;
< dvi_f←f;
< font_used[f]←true;
---
> begin if not font_used[f] then
> begin dvi_font_def(f); font_used[f]:=true;
> end;
> if f<=64+font_base then dvi_out(f-font_base-1+fnt_num_0)
> else begin dvi_out(fnt1); dvi_out(f-font_base-1);
> end;
> dvi_f:=f;
9888,9890c12288,12290
< rule_node: begin rule_ht←height(p); rule_dp←depth(p); rule_wd←width(p);
< goto fin_rule;
< end;
---
> rule_node: begin rule_ht:=height(p); rule_dp:=depth(p); rule_wd:=width(p);
> goto fin_rule;
> end;
9893c12293
< kern_node,math_node:cur_h←cur_h+width(p);
---
> kern_node,math_node:cur_h:=cur_h+width(p);
9899,9900c12299,12300
< advance: cur_h←cur_h+rule_wd;
< next_p:p←link(p);
---
> move_past: cur_h:=cur_h+rule_wd;
> next_p:p:=link(p);
9904,9911c12304,12311
< if list_ptr(p)=null then cur_h←cur_h+width(p)
< else begin save_h←dvi_h; save_v←dvi_v; dvi_out(push);
< cur_v←base_line+shift_amount(p); {shift the box down}
< temp_ptr←p; edge←cur_h;
< if type(p)=vlist_node then vlist_out@+else hlist_out;
< dvi_out(pop); dvi_h←save_h; dvi_v←save_v;
< cur_h←edge+width(p); cur_v←base_line;
< end
---
> if list_ptr(p)=null then cur_h:=cur_h+width(p)
> else begin save_h:=dvi_h; save_v:=dvi_v;
> cur_v:=base_line+shift_amount(p); {shift the box down}
> temp_ptr:=p; edge:=cur_h;
> if type(p)=vlist_node then vlist_out@+else hlist_out;
> dvi_h:=save_h; dvi_v:=save_v;
> cur_h:=edge+width(p); cur_v:=base_line;
> end
9914,9937c12314,12351
< if is_running(rule_ht) then rule_ht←height(this_box);
< if is_running(rule_dp) then rule_dp←depth(this_box);
< rule_ht←rule_ht+rule_dp; {this is the rule thickness}
< if (rule_ht>0)∧(rule_wd>0) then {we don't output empty rules}
< begin synch_h; cur_v←base_line+rule_dp; synch_v;
< dvi_out(set_rule); dvi_four(rule_ht); dvi_four(rule_wd);
< cur_v←base_line; dvi_h←dvi_h+rule_wd;
< end
<
< @ @<Move right or output leaders@>=
< begin g←glue_ptr(p); rule_wd←width(g);
< if g_sign≠normal then
< begin if g_sign=stretching then
< begin if stretch_order(g)=g_order then
< rule_wd←rule_wd+round(glue_set(this_box)*stretch(g));
< end
< else begin if shrink_order(g)=g_order then
< rule_wd←rule_wd-round(glue_set(this_box)*shrink(g));
< end;
< end;
< if subtype(p)≥a_leaders then
< @<Output leaders in an hlist, |goto fin_rule| if a rule
< or to |next_p| if done@>;
< goto advance;
---
> if is_running(rule_ht) then rule_ht:=height(this_box);
> if is_running(rule_dp) then rule_dp:=depth(this_box);
> rule_ht:=rule_ht+rule_dp; {this is the rule thickness}
> if (rule_ht>0)and(rule_wd>0) then {we don't output empty rules}
> begin synch_h; cur_v:=base_line+rule_dp; synch_v;
> dvi_out(set_rule); dvi_four(rule_ht); dvi_four(rule_wd);
> cur_v:=base_line; dvi_h:=dvi_h+rule_wd;
> end
>
> @ @d billion==float_constant(1000000000)
> @d vet_glue(#)== glue_temp:=#;
> if glue_temp>billion then
> glue_temp:=billion
> else if glue_temp<-billion then
> glue_temp:=-billion
>
> @<Move right or output leaders@>=
> begin g:=glue_ptr(p); rule_wd:=width(g)-cur_g;
> if g_sign<>normal then
> begin if g_sign=stretching then
> begin if stretch_order(g)=g_order then
> begin cur_glue:=cur_glue+stretch(g);
> vet_glue(float(glue_set(this_box))*cur_glue);
> @^real multiplication@>
> cur_g:=round(glue_temp);
> end;
> end
> else if shrink_order(g)=g_order then
> begin cur_glue:=cur_glue-shrink(g);
> vet_glue(float(glue_set(this_box))*cur_glue);
> cur_g:=round(glue_temp);
> end;
> end;
> rule_wd:=rule_wd+cur_g;
> if subtype(p)>=a_leaders then
> @<Output leaders in an hlist, |goto fin_rule| if a rule
> or to |next_p| if done@>;
> goto move_past;
9941c12355
< begin leader_box←leader_ptr(p);
---
> begin leader_box:=leader_ptr(p);
9943,9955c12357,12370
< begin rule_ht←height(leader_box); rule_dp←depth(leader_box);
< goto fin_rule;
< end;
< leader_wd←width(leader_box);
< if (leader_wd>0)∧(rule_wd>0) then
< begin edge←cur_h+rule_wd;
< @<Let |cur_h| be the position of the first box, and set |leader_wd|
< to the spacing between corresponding parts of boxes@>;
< while cur_h+leader_wd≤edge do
< @<Output a leader box at |cur_h|,
< then advance |cur_h| by |leader_wd|@>;
< cur_h←edge; goto next_p;
< end;
---
> begin rule_ht:=height(leader_box); rule_dp:=depth(leader_box);
> goto fin_rule;
> end;
> leader_wd:=width(leader_box);
> if (leader_wd>0)and(rule_wd>0) then
> begin rule_wd:=rule_wd+10; {compensate for floating-point rounding}
> edge:=cur_h+rule_wd; lx:=0;
> @<Let |cur_h| be the position of the first box, and set |leader_wd+lx|
> to the spacing between corresponding parts of boxes@>;
> while cur_h+leader_wd<=edge do
> @<Output a leader box at |cur_h|,
> then advance |cur_h| by |leader_wd+lx|@>;
> cur_h:=edge-10; goto next_p;
> end;
9959,9963c12374,12380
< case of |a_leaders| (aligned leaders), we want to move |cur_h| to the
< smallest multiple of |leader_wd| that is not less than the current
< value of |cur_h|, namely $|leader_wd|\ast\lceil |cur_h|/|leader_wd|\rceil$.
< The program here should work in all cases even though \PASCAL\ does not define
< the |div| operation precisely, and even when |cur_h| is negative.
---
> case of |a_leaders| (aligned leaders), we want to move |cur_h| to
> |left_edge| plus the smallest multiple of |leader_wd| for which the result
> is not less than the current value of |cur_h|; i.e., |cur_h| should become
> $|left_edge|+|leader_wd|\times\lceil
> (|cur_h|-|left_edge|)/|leader_wd|\rceil$. The program here should work in
> all cases even though some implementations of \PASCAL\ give nonstandard
> results for the |div| operation when |cur_h| is less than |left_edge|.
9966,9967c12383,12384
< by half of the excess space not occupied by the leaders; and in the case of
< case of |x_leaders| (expanded leaders) we increase both |cur_h| and |leader_wd|
---
> by half of the excess space not occupied by the leaders; and in the
> case of |x_leaders| (expanded leaders) we increase |cur_h|
9974,9984c12391,12401
< begin save_h←cur_h; cur_h←leader_wd*(cur_h @!div leader_wd);
< if cur_h<save_h then cur_h←cur_h+leader_wd;
< end
< else begin lq←rule_wd div leader_wd; {the number of box copies}
< lr←rule_wd mod leader_wd; {the remaining space}
< if subtype(p)=c_leaders then cur_h←cur_h+(lr div 2)
< else begin lx←(2*lr+lq+1) div (2*lq+2); {round|(lr/(q+1))|}
< leader_wd←leader_wd+lx;
< cur_h←cur_h+((lr-(lq-1)*lx) div 2);
< end;
< end
---
> begin save_h:=cur_h;
> cur_h:=left_edge+leader_wd*((cur_h-left_edge)@!div leader_wd);
> if cur_h<save_h then cur_h:=cur_h+leader_wd;
> end
> else begin lq:=rule_wd div leader_wd; {the number of box copies}
> lr:=rule_wd mod leader_wd; {the remaining space}
> if subtype(p)=c_leaders then cur_h:=cur_h+(lr div 2)
> else begin lx:=lr div (lq+1);
> cur_h:=cur_h+((lr-(lq-1)*lx) div 2);
> end;
> end
9986,9987c12403,12404
< @ The `\\{synch}' operations here are intended to decrease the number
< of bytes needed to specify horizontal and vertical motion in the \.{DVI} output.
---
> @ The `\\{synch}' operations here are intended to decrease the number of
> bytes needed to specify horizontal and vertical motion in the \.{DVI} output.
9990,9992c12407,12409
< begin cur_v←base_line+shift_amount(leader_box); synch_v; save_v←dvi_v;@/
< synch_h; save_h←dvi_h; dvi_out(push); temp_ptr←leader_box;
< outer_doing_leaders←doing_leaders; doing_leaders←true;
---
> begin cur_v:=base_line+shift_amount(leader_box); synch_v; save_v:=dvi_v;@/
> synch_h; save_h:=dvi_h; temp_ptr:=leader_box;
> outer_doing_leaders:=doing_leaders; doing_leaders:=true;
9994,9996c12411,12413
< doing_leaders←outer_doing_leaders;
< dvi_out(pop); dvi_v←save_v; dvi_h←save_h; cur_v←save_v;
< cur_h←save_h+leader_wd;
---
> doing_leaders:=outer_doing_leaders;
> dvi_v:=save_v; dvi_h:=save_h; cur_v:=base_line;
> cur_h:=save_h+leader_wd+lx;
10002c12419
< label advance, fin_rule, next_p;
---
> label move_past, fin_rule, next_p;
10003a12421
> @!top_edge: scaled; {the top coordinate for this box}
10011a12430
> @!lx:scaled; {extra space between leader boxes}
10014,10015c12433,12438
< begin this_box←temp_ptr; g_order←glue_order(this_box);
< g_sign←glue_sign(this_box); p←list_ptr(this_box);
---
> @!glue_temp:real; {glue value before rounding}
> @!cur_glue:real; {glue seen so far}
> @!cur_g:scaled; {rounded equivalent of |cur_glue| times the glue ratio}
> begin cur_g:=0; cur_glue:=float_constant(0);
> this_box:=temp_ptr; g_order:=glue_order(this_box);
> g_sign:=glue_sign(this_box); p:=list_ptr(this_box);
10017,10021c12440,12448
< if cur_s>max_push then max_push←cur_s;
< save_loc←dvi_offset+dvi_ptr; left_edge←cur_h; cur_v←cur_v-height(this_box);
< while p≠null do @<Output node |p| for |vlist_out| and move to the next node,
< maintaining the condition |cur_h=left_edge|@>;
< prune_movements(save_loc); decr(cur_s);
---
> if cur_s>0 then dvi_out(push);
> if cur_s>max_push then max_push:=cur_s;
> save_loc:=dvi_offset+dvi_ptr; left_edge:=cur_h; cur_v:=cur_v-height(this_box);
> top_edge:=cur_v;
> while p<>null do @<Output node |p| for |vlist_out| and move to the next node,
> maintaining the condition |cur_h=left_edge|@>;
> prune_movements(save_loc);
> if cur_s>0 then dvi_pop(save_loc);
> decr(cur_s);
10026c12453
< @:confusion vlistout}{\quad vlistout@>
---
> @:this can't happen vlistout}{\quad vlistout@>
10028c12455
< next_p:p←link(p);
---
> next_p:p:=link(p);
10034,10036c12461,12463
< rule_node: begin rule_ht←height(p); rule_dp←depth(p); rule_wd←width(p);
< goto fin_rule;
< end;
---
> rule_node: begin rule_ht:=height(p); rule_dp:=depth(p); rule_wd:=width(p);
> goto fin_rule;
> end;
10039c12466
< kern_node:cur_v←cur_v+width(p);
---
> kern_node:cur_v:=cur_v+width(p);
10044c12471
< advance: cur_v←cur_v+rule_ht;
---
> move_past: cur_v:=cur_v+rule_ht;
10052,10060c12479,12487
< if list_ptr(p)=null then cur_v←cur_v+height(p)+depth(p)
< else begin cur_v←cur_v+height(p); synch_v;
< save_h←dvi_h; save_v←dvi_v; dvi_out(push);
< cur_h←left_edge+shift_amount(p); {shift the box right}
< temp_ptr←p;
< if type(p)=vlist_node then vlist_out@+else hlist_out;
< dvi_out(pop); dvi_h←save_h; dvi_v←save_v;
< cur_v←save_v+depth(p); cur_h←left_edge;
< end
---
> if list_ptr(p)=null then cur_v:=cur_v+height(p)+depth(p)
> else begin cur_v:=cur_v+height(p); synch_v;
> save_h:=dvi_h; save_v:=dvi_v;
> cur_h:=left_edge+shift_amount(p); {shift the box right}
> temp_ptr:=p;
> if type(p)=vlist_node then vlist_out@+else hlist_out;
> dvi_h:=save_h; dvi_v:=save_v;
> cur_v:=save_v+depth(p); cur_h:=left_edge;
> end
10063,10069c12490,12496
< if is_running(rule_wd) then rule_wd←width(this_box);
< rule_ht←rule_ht+rule_dp; {this is the rule thickness}
< cur_v←cur_v+rule_ht;
< if (rule_ht>0)∧(rule_wd>0) then {we don't output empty rules}
< begin synch_h; synch_v;
< dvi_out(put_rule); dvi_four(rule_ht); dvi_four(rule_wd);
< end;
---
> if is_running(rule_wd) then rule_wd:=width(this_box);
> rule_ht:=rule_ht+rule_dp; {this is the rule thickness}
> cur_v:=cur_v+rule_ht;
> if (rule_ht>0)and(rule_wd>0) then {we don't output empty rules}
> begin synch_h; synch_v;
> dvi_out(put_rule); dvi_four(rule_ht); dvi_four(rule_wd);
> end;
10073,10086c12500,12520
< begin g←glue_ptr(p); rule_ht←width(g);
< if g_sign≠normal then
< begin if g_sign=stretching then
< begin if stretch_order(g)=g_order then
< rule_ht←rule_ht+round(glue_set(this_box)*stretch(g));
< end
< else begin if shrink_order(g)=g_order then
< rule_ht←rule_ht-round(glue_set(this_box)*shrink(g));
< end;
< end;
< if subtype(p)≥a_leaders then
< @<Output leaders in a vlist, |goto fin_rule| if a rule
< or to |next_p| if done@>;
< goto advance;
---
> begin g:=glue_ptr(p); rule_ht:=width(g)-cur_g;
> if g_sign<>normal then
> begin if g_sign=stretching then
> begin if stretch_order(g)=g_order then
> begin cur_glue:=cur_glue+stretch(g);
> vet_glue(float(glue_set(this_box))*cur_glue);
> @^real multiplication@>
> cur_g:=round(glue_temp);
> end;
> end
> else if shrink_order(g)=g_order then
> begin cur_glue:=cur_glue-shrink(g);
> vet_glue(float(glue_set(this_box))*cur_glue);
> cur_g:=round(glue_temp);
> end;
> end;
> rule_ht:=rule_ht+cur_g;
> if subtype(p)>=a_leaders then
> @<Output leaders in a vlist, |goto fin_rule| if a rule
> or to |next_p| if done@>;
> goto move_past;
10090c12524
< begin leader_box←leader_ptr(p);
---
> begin leader_box:=leader_ptr(p);
10092,10104c12526,12539
< begin rule_wd←width(leader_box); rule_dp←0;
< goto fin_rule;
< end;
< leader_ht←height(leader_box)+depth(leader_box);
< if (leader_ht>0)∧(rule_ht>0) then
< begin edge←cur_v+rule_ht;
< @<Let |cur_v| be the position of the first box, and set |leader_ht|
< to the spacing between corresponding parts of boxes@>;
< while cur_v+leader_ht≤edge do
< @<Output a leader box at |cur_v|,
< then advance |cur_v| by |leader_ht|@>;
< cur_v←edge; goto next_p;
< end;
---
> begin rule_wd:=width(leader_box); rule_dp:=0;
> goto fin_rule;
> end;
> leader_ht:=height(leader_box)+depth(leader_box);
> if (leader_ht>0)and(rule_ht>0) then
> begin rule_ht:=rule_ht+10; {compensate for floating-point rounding}
> edge:=cur_v+rule_ht; lx:=0;
> @<Let |cur_v| be the position of the first box, and set |leader_ht+lx|
> to the spacing between corresponding parts of boxes@>;
> while cur_v+leader_ht<=edge do
> @<Output a leader box at |cur_v|,
> then advance |cur_v| by |leader_ht+lx|@>;
> cur_v:=edge-10; goto next_p;
> end;
10109,10119c12544,12554
< begin save_v←cur_v; cur_v←leader_ht*(cur_v @!div leader_ht);
< if cur_v<save_v then cur_v←cur_v+leader_ht;
< end
< else begin lq←rule_ht div leader_ht; {the number of box copies}
< lr←rule_ht mod leader_ht; {the remaining space}
< if subtype(p)=c_leaders then cur_v←cur_v+(lr div 2)
< else begin lx←(2*lr+lq+1) div (2*lq+2); {round|(lr/(q+1))|}
< leader_ht←leader_ht+lx;
< cur_v←cur_v+((lr-(lq-1)*lx) div 2);
< end;
< end
---
> begin save_v:=cur_v;
> cur_v:=top_edge+leader_ht*((cur_v-top_edge)@!div leader_ht);
> if cur_v<save_v then cur_v:=cur_v+leader_ht;
> end
> else begin lq:=rule_ht div leader_ht; {the number of box copies}
> lr:=rule_ht mod leader_ht; {the remaining space}
> if subtype(p)=c_leaders then cur_v:=cur_v+(lr div 2)
> else begin lx:=lr div (lq+1);
> cur_v:=cur_v+((lr-(lq-1)*lx) div 2);
> end;
> end
10125,10128c12560,12563
< begin cur_h←left_edge+shift_amount(leader_box); synch_h; save_h←dvi_h;@/
< cur_v←cur_v+height(leader_box); synch_v; save_v←dvi_v;
< dvi_out(push); temp_ptr←leader_box;
< outer_doing_leaders←doing_leaders; doing_leaders←true;
---
> begin cur_h:=left_edge+shift_amount(leader_box); synch_h; save_h:=dvi_h;@/
> cur_v:=cur_v+height(leader_box); synch_v; save_v:=dvi_v;
> temp_ptr:=leader_box;
> outer_doing_leaders:=doing_leaders; doing_leaders:=true;
10130,10132c12565,12567
< doing_leaders←outer_doing_leaders;
< dvi_out(pop); dvi_v←save_v; dvi_h←save_h; cur_h←save_h;
< cur_v←save_v-height(leader_box)+leader_ht;
---
> doing_leaders:=outer_doing_leaders;
> dvi_v:=save_v; dvi_h:=save_h; cur_h:=left_edge;
> cur_v:=save_v-height(leader_box)+leader_ht+lx;
10138a12574
> label done;
10140,10145c12576,12581
< @!k:0..9; {runs through ten count registers}
< begin if tracing_output=0 then
< begin if offset>max_print_line-9 then print_ln@+else print_char(" ");
< print_char("["); print_int(count(0)); update_terminal;
< end
< else begin print_nl(""); print_nl("Completed box being shipped out [");
---
> @!j,@!k:0..9; {indices to first ten count registers}
> @!s:pool_pointer; {index into |str_pool|}
> @!old_setting:0..max_selector; {saved |selector| setting}
> begin if tracing_output>0 then
> begin print_nl(""); print_ln;
> print("Completed box being shipped out");
10147,10153c12583,12596
< for k←0 to 9 do
< begin print_int(count(k));
< if k<9 then print_char(".");
< end;
< print_char("]");@/
< begin_diagnostic; show_box(p); end_diagnostic;
< end;
---
> end;
> if term_offset>max_print_line-9 then print_ln
> else if (term_offset>0)or(file_offset>0) then print_char(" ");
> print_char("["); j:=9;
> while (count(j)=0)and(j>0) do decr(j);
> for k:=0 to j do
> begin print_int(count(k));
> if k<j then print_char(".");
> end;
> update_terminal;
> if tracing_output>0 then
> begin print_char("]");
> begin_diagnostic; show_box(p); end_diagnostic(true);
> end;
10155c12598,12600
< if tracing_output=0 then print_char("]"); update_terminal; {progress report}
---
> if tracing_output<=0 then print_char("]");
> dead_cycles:=0;
> update_terminal; {progress report}
10160,10164c12605,12610
< stat if tracing_stats≠0 then
< begin print_nl("Memory usage before: ");
< print_int(var_used); print_char(",");
< print_int(dyn_used); print_char(";");
< end;
---
> @!stat if tracing_stats>1 then
> begin print_nl("Memory usage before: ");
> @.Memory usage...@>
> print_int(var_used); print_char("&");
> print_int(dyn_used); print_char(";");
> end;
10167,10173c12613,12618
< stat if tracing_stats≠0 then
< begin print(" after: ");
< print_int(var_used); print_char(",");
< print_int(dyn_used); print("; max so far: ");
< print_int(max_var_used); print_char(",");
< print_int(mem_end+1-hi_mem_base); print_ln;
< end;
---
> @!stat if tracing_stats>1 then
> begin print(" after: ");
> print_int(var_used); print_char("&");
> print_int(dyn_used); print("; still untouched: ");
> print_int(hi_mem_min-lo_mem_max-1); print_ln;
> end;
10177,10178c12622,12623
< if height(p)+depth(p)>max_v then max_v←height(p)+depth(p);
< if width(p)>max_h then max_h←width(p);
---
> @<Update the values of |max_h| and |max_v|; but if the page is too large,
> |goto done|@>;
10180c12625
< page_loc←dvi_offset+dvi_ptr;
---
> page_loc:=dvi_offset+dvi_ptr;
10182,10184c12627,12629
< for k←0 to 9 do dvi_four(count(k));
< dvi_four(last_bop); last_bop←page_loc;
< cur_v←height(p); temp_ptr←p;
---
> for k:=0 to 9 do dvi_four(count(k));
> dvi_four(last_bop); last_bop:=page_loc;
> cur_v:=height(p)+v_offset; temp_ptr:=p;
10186c12631,12657
< dvi_out(eop); incr(total_pages)
---
> dvi_out(eop); incr(total_pages); cur_s:=-1;
> done:
>
> @ Sometimes the user will generate a huge page because other error messages
> are being ignored. Such pages are not output to the \.{dvi} file, since they
> may confuse the printing software.
>
> @<Update the values of |max_h| and |max_v|; but if the page is too large...@>=
> if (height(p)>max_dimen)or@|(depth(p)>max_dimen)or@|
> (height(p)+depth(p)+v_offset>max_dimen)or@|
> (width(p)+h_offset>max_dimen) then
> begin print_err("Huge page cannot be shipped out");
> @.Huge page...@>
> help2("The page just created is more than 18 feet tall or")@/
> ("more than 18 feet wide, so I suspect something went wrong.");
> error;
> if tracing_output<=0 then
> begin begin_diagnostic;
> print_nl("The following box has been deleted:");
> @.The following...deleted@>
> show_box(p);
> end_diagnostic(true);
> end;
> goto done;
> end;
> if height(p)+depth(p)+v_offset>max_v then max_v:=height(p)+depth(p)+v_offset;
> if width(p)+h_offset>max_h then max_h:=width(p)+h_offset
10189a12661,12662
> If |total_pages>=65536|, the \.{DVI} file will lie. And if
> |max_push>=65536|, the user deserves whatever chaos might ensue.
10194,10212c12667,12695
< if total_pages=0 then print_nl("No output file.")
< else begin dvi_out(pst); {beginning of the postamble}
< dvi_four(last_bop); last_bop←dvi_offset+dvi_ptr-5; {|pst| location}
< dvi_four(25400000); dvi_four(473628672); {conversion ratio for sp}
< prepare_mag; dvi_four(mag); {magnification factor}
< dvi_four(max_v); dvi_four(max_h);@/
< dvi_out(max_push div 256); dvi_out(max_push mod 256);@/
< dvi_out(total_pages div 256); dvi_out(total_pages mod 256);@/
< @<Output the font definitions for all fonts that were used@>;
< dvi_four(-1); dvi_four(last_bop); dvi_out(id_byte);
< k←4+((dvi_buf_size-dvi_ptr) mod 4); {the number of 223's}
< while k>0 do
< begin dvi_out(223); decr(k);
< end;
< @<Empty the last bytes out of |dvi_buf|@>;
< print_nl("Output written on "); print(output_file_name);
< print(" ("); print_int(total_pages); print(" pages, ");
< print_int(dvi_offset+dvi_ptr); print(" bytes)."); b_close(dvi_file);
< end
---
> while cur_s>-1 do
> begin if cur_s>0 then dvi_out(pop)
> else begin dvi_out(eop); incr(total_pages);
> end;
> decr(cur_s);
> end;
> if total_pages=0 then print_nl("No pages of output.")
> @.No pages of output@>
> else begin dvi_out(post); {beginning of the postamble}
> dvi_four(last_bop); last_bop:=dvi_offset+dvi_ptr-5; {|post| location}
> dvi_four(25400000); dvi_four(473628672); {conversion ratio for sp}
> prepare_mag; dvi_four(mag); {magnification factor}
> dvi_four(max_v); dvi_four(max_h);@/
> dvi_out(max_push div 256); dvi_out(max_push mod 256);@/
> dvi_out((total_pages div 256) mod 256); dvi_out(total_pages mod 256);@/
> @<Output the font definitions for all fonts that were used@>;
> dvi_out(post_post); dvi_four(last_bop); dvi_out(id_byte);@/
> k:=4+((dvi_buf_size-dvi_ptr) mod 4); {the number of 223's}
> while k>0 do
> begin dvi_out(223); decr(k);
> end;
> @<Empty the last bytes out of |dvi_buf|@>;
> print_nl("Output written on "); slow_print(output_file_name);
> @.Output written on x@>
> print(" ("); print_int(total_pages); print(" page");
> if total_pages<>1 then print_char("s");
> print(", "); print_int(dvi_offset+dvi_ptr); print(" bytes).");
> b_close(dvi_file);
> end
10216,10234c12699,12703
< begin if font_used[font_ptr] then
< begin dvi_four(font_ptr-font_base-1);
< dvi_out(qo(font_check[font_ptr].b0));
< dvi_out(qo(font_check[font_ptr].b1));
< dvi_out(qo(font_check[font_ptr].b2));
< dvi_out(qo(font_check[font_ptr].b3));
< dvi_four(font_size[font_ptr]);
< dvi_out(length(font_area[font_ptr]));
< dvi_out(length(font_name[font_ptr]));
< @<Output the font name whose internal number is |font_ptr|@>;
< end;
< decr(font_ptr);
< end
< @ @<Output the font name whose internal number is |font_ptr|@>=
< for k←str_start[font_area[font_ptr]] to str_start[font_area[font_ptr]+1]-1 do
< dvi_out(str_pool[k]);
< for k←str_start[font_name[font_ptr]] to str_start[font_name[font_ptr]+1]-1 do
< dvi_out(str_pool[k])
< @* \[32] Packaging.
---
> begin if font_used[font_ptr] then dvi_font_def(font_ptr);
> decr(font_ptr);
> end
>
> @* \[33] Packaging.
10245c12714
< inside the new box, but some items may stick out if negative glue is used,
---
> inside the new box; but some items may stick out if negative glue is used,
10251,10252c12720,12721
< a width; and pa\-ram\-eter |m| is either `|exactly|' or `|additional|'. Thus,
< |hpack(p,w,exactly)| pro\-duces a box whose width is exactly |w|, while
---
> a width; and parameter |m| is either `|exactly|' or `|additional|'. Thus,
> |hpack(p,w,exactly)| produces a box whose width is exactly |w|, while
10267c12736
< like `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{expand} \.{10pt}'; note
---
> like `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note
10269c12738
< `\.{\\hbox} \.{expand} \.{0pt}'. The |scan_spec| subroutine scans such
---
> `\.{\\hbox} \.{spread} \.{0pt}'. The |scan_spec| subroutine scans such
10274c12743
< |save_ptr←save_ptr-2;|\cr
---
> |save_ptr:=save_ptr-2;|\cr
10275a12745,12747
> Special care is necessary to ensure that the special |save_stack| codes
> are placed just below the new group code, because scanning can change
> |save_stack| when \.{\\csname} appears.
10277c12749,12750
< @p procedure scan_spec; {scans a box specification and left brace}
---
> @p procedure scan_spec(@!c:group_code;@!three_codes:boolean);
> {scans a box specification and left brace}
10279,10285c12752,12767
< begin if scan_keyword("to") then saved(0)←exactly
< else if scan_keyword("expand") then saved(0)←additional
< else begin saved(0)←additional; saved(1)←0;
< goto found;
< end;
< scan_dimen(false,false,false); saved(1)←cur_val;
< found: save_ptr←save_ptr+2; scan_left_brace;
---
> var @!s:integer; {temporarily saved value}
> @!spec_code:exactly..additional;
> begin if three_codes then s:=saved(0);
> if scan_keyword("to") then spec_code:=exactly
> @.to@>
> else if scan_keyword("spread") then spec_code:=additional
> @.spread@>
> else begin spec_code:=additional; cur_val:=0;
> goto found;
> end;
> scan_normal_dimen;
> found: if three_codes then
> begin saved(0):=s; incr(save_ptr);
> end;
> saved(0):=spec_code; saved(1):=cur_val; save_ptr:=save_ptr+2;
> new_save_level(c); scan_left_brace;
10302c12784,12785
< glue of each kind is present.
---
> glue of each kind is present. A global variable |last_badness| is used
> to implement \.{\\badness}.
10306c12789,12795
< {glue found by |hpack| or |vpack|}
---
> {glue found by |hpack| or |vpack|}
> @!last_badness:integer; {badness of the most recently packaged box}
>
> @ If the global variable |adjust_tail| is non-null, the |hpack| routine
> also removes all occurrences of |ins_node|, |mark_node|, and |adjust_node|
> items and appends the resulting material onto the list that ends at
> location |adjust_tail|.
10308,10315c12797,12798
< @ Here now is |hpack| itself, which contains only one possible surprise:
< Be\-sides making a list into a box, |hpack| also removes all occurrences of
< |ins_node|, |mark_node|, and |adjust_node| items; it puts the resulting
< material into a list that starts at location |adjustments=link(adjust_head)|.
< These adjustments will be put into the enclosing vertical list, if |hpack|
< is being called to make a line of a paragraph; otherwise such nodes cannot
< be present in a horizontal list, since they are syntactically forbidden in
< restricted horizontal mode.
---
> @< Glob...@>=
> @!adjust_tail:pointer; {tail of adjustment list}
10317c12800,12802
< @d adjustments==link(adjust_head)
---
> @ @<Set init...@>=adjust_tail:=null; last_badness:=0;
>
> @ Here now is |hpack|, which contains few if any surprises.
10329,10339c12814,12823
< @!hd:quarterword; {height and depth indices for a character}
< @!t:pointer; {tail of the adjustment list}
< @!b:integer; {badness of the new box}
< begin r←get_node(box_node_size); type(r)←hlist_node;
< subtype(r)←min_quarterword; shift_amount(r)←0;
< q←r+list_offset; link(q)←p;@/
< t←adjust_head; h←0; @<Clear dimensions to zero@>;
< while p≠null do @<Examine node |p| in the hlist, taking account of its effect
< on the dimensions of the new box, or moving it to the adjustment list;
< then advance |p| to the next node@>;
< link(t)←null; height(r)←h; depth(r)←d;@/
---
> @!hd:eight_bits; {height and depth indices for a character}
> begin last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node;
> subtype(r):=min_quarterword; shift_amount(r):=0;
> q:=r+list_offset; link(q):=p;@/
> h:=0; @<Clear dimensions to zero@>;
> while p<>null do @<Examine node |p| in the hlist, taking account of its effect
> on the dimensions of the new box, or moving it to the adjustment list;
> then advance |p| to the next node@>;
> if adjust_tail<>null then link(adjust_tail):=null;
> height(r):=h; depth(r):=d;@/
10341c12825
< then |return| or |goto common_ending|@>;
---
> then |return| or |goto common_ending|@>;
10343,10344c12827,12828
< for an overfull or underfull hbox@>;
< exit: hpack←r;
---
> for an overfull or underfull hbox@>;
> exit: hpack:=r;
10348,10351c12832,12836
< d←0; x←0;
< for o←normal to filll do
< begin total_stretch[o]←0; total_shrink[o]←0;
< end
---
> d:=0; x:=0;
> total_stretch[normal]:=0; total_shrink[normal]:=0;
> total_stretch[fil]:=0; total_shrink[fil]:=0;
> total_stretch[fill]:=0; total_shrink[fill]:=0;
> total_stretch[filll]:=0; total_shrink[filll]:=0
10353a12839
> @^inner loop@>
10355,10372c12841,12858
< @<Incorporate character dimensions into the dimensions of
< the hbox that will contain@@it, then move to the next node@>;
< if p≠null then
< begin case type(p) of
< hlist_node,vlist_node,rule_node,unset_node:
< @<Incorporate box dimensions into the dimensions of
< the hbox that will contain@@it@>;
< ins_node,mark_node,adjust_node:
< @<Transfer node |p| to the adjustment list@>;
< whatsit_node:@<Incorporate a whatsit node into an hbox@>;
< glue_node:@<Incorporate glue into the totals@>;
< kern_node,math_node: x←x+width(p);
< ligature_node: @<Make node |p| look like a |char_node|
< and |goto reswitch|@>;
< othercases do_nothing
< endcases;@/
< p←link(p);
< end;
---
> @<Incorporate character dimensions into the dimensions of
> the hbox that will contain~it, then move to the next node@>;
> if p<>null then
> begin case type(p) of
> hlist_node,vlist_node,rule_node,unset_node:
> @<Incorporate box dimensions into the dimensions of
> the hbox that will contain~it@>;
> ins_node,mark_node,adjust_node: if adjust_tail<>null then
> @<Transfer node |p| to the adjustment list@>;
> whatsit_node:@<Incorporate a whatsit node into an hbox@>;
> glue_node:@<Incorporate glue into the horizontal totals@>;
> kern_node,math_node: x:=x+width(p);
> ligature_node: @<Make node |p| look like a |char_node|
> and |goto reswitch|@>;
> othercases do_nothing
> endcases;@/
> p:=link(p);
> end;
10377,10378c12863,12864
< begin mem[lig_trick]←mem[lig_char(p)]; link(lig_trick)←link(p);
< p←lig_trick; goto reswitch;
---
> begin mem[lig_trick]:=mem[lig_char(p)]; link(lig_trick):=link(p);
> p:=lig_trick; goto reswitch;
10381,10385c12867,12875
< @ @<Incorporate box dimensions into the dimensions of the hbox...@>=
< begin x←x+width(p);
< if type(p)≥rule_node then s←0 @+else s←shift_amount(p);
< if height(p)-s>h then h←height(p)-s;
< if depth(p)+s>d then d←depth(p)+s;
---
> @ The code here implicitly uses the fact that running dimensions are
> indicated by |null_flag|, which will be ignored in the calculations
> because it is a highly negative number.
>
> @<Incorporate box dimensions into the dimensions of the hbox...@>=
> begin x:=x+width(p);
> if type(p)>=rule_node then s:=0 @+else s:=shift_amount(p);
> if height(p)-s>h then h:=height(p)-s;
> if depth(p)+s>d then d:=depth(p)+s;
10394,10398c12884,12888
< begin f←font(p); i←char_info(f)(character(p)); hd←height_depth(i);
< x←x+char_width(f)(i);@/
< s←char_height(f)(hd);@+if s>h then h←s;
< s←char_depth(f)(hd);@+if s>d then d←s;
< p←link(p);
---
> begin f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i);
> x:=x+char_width(f)(i);@/
> s:=char_height(f)(hd);@+if s>h then h:=s;
> s:=char_depth(f)(hd);@+if s>d then d:=s;
> p:=link(p);
10405a12896
> @^inner loop@>
10408c12899
< begin while link(q)≠p do q←link(q);
---
> begin while link(q)<>p do q:=link(q);
10410,10422c12901,12918
< begin link(t)←adjust_ptr(p);
< while link(t)≠null do t←link(t);
< p←link(p); free_node(link(q),small_node_size);
< end
< else begin link(t)←p; t←p; p←link(p);
< end;
< link(q)←p; p←q;
< end
<
< @ @<Incorporate glue into the totals@>=
< begin g←glue_ptr(p); x←x+width(g);@/
< o←stretch_order(g); total_stretch[o]←total_stretch[o]+stretch(g);
< o←shrink_order(g); total_shrink[o]←total_shrink[o]+shrink(g);
---
> begin link(adjust_tail):=adjust_ptr(p);
> while link(adjust_tail)<>null do adjust_tail:=link(adjust_tail);
> p:=link(p); free_node(link(q),small_node_size);
> end
> else begin link(adjust_tail):=p; adjust_tail:=p; p:=link(p);
> end;
> link(q):=p; p:=q;
> end
>
> @ @<Incorporate glue into the horizontal totals@>=
> begin g:=glue_ptr(p); x:=x+width(g);@/
> o:=stretch_order(g); total_stretch[o]:=total_stretch[o]+stretch(g);
> o:=shrink_order(g); total_shrink[o]:=total_shrink[o]+shrink(g);
> if subtype(p)>=a_leaders then
> begin g:=leader_ptr(p);
> if height(g)>h then h:=height(g);
> if depth(g)>d then d:=depth(g);
> end;
10429,10430c12925,12926
< if m=additional then w←x+w;
< width(r)←w; x←w-x; {now |x| is the excess to be made up}
---
> if m=additional then w:=x+w;
> width(r):=w; x:=w-x; {now |x| is the excess to be made up}
10432,10434c12928,12931
< begin glue_sign(r)←normal; glue_order(r)←normal; glue_set(r)←0.0;
< return;
< end
---
> begin glue_sign(r):=normal; glue_order(r):=normal;
> set_glue_ratio_zero(glue_set(r));
> return;
> end
10436c12933
< or \hbox{|goto common_ending|}@>
---
> or \hbox{|goto common_ending|}@>
10438c12935
< or \hbox{|goto common_ending|}@>
---
> or \hbox{|goto common_ending|}@>
10442,10447c12939,12947
< glue_order(r)←o; glue_sign(r)←stretching;
< if total_stretch[o]=0 then glue_set(r)←0.0 {there's nothing to stretch}
< else glue_set(r)←x/total_stretch[o];
< if (hbadness<inf_bad)∧(o=normal)∧(list_ptr(r)≠null) then
< @<Report an underfull hbox and |goto common_ending|, if this box
< is sufficiently bad@>;
---
> glue_order(r):=o; glue_sign(r):=stretching;
> if total_stretch[o]<>0 then glue_set(r):=unfloat(x/total_stretch[o])
> @^real division@>
> else begin glue_sign(r):=normal;
> set_glue_ratio_zero(glue_set(r)); {there's nothing to stretch}
> end;
> if o=normal then if list_ptr(r)<>null then
> @<Report an underfull hbox and |goto common_ending|, if this box
> is sufficiently bad@>;
10452,10455c12952,12955
< if total_stretch[filll]≠0 then o←filll
< else if total_stretch[fill]≠0 then o←fill
< else if total_stretch[fil]≠0 then o←fil
< else o←normal
---
> if total_stretch[filll]<>0 then o:=filll
> else if total_stretch[fill]<>0 then o:=fill
> else if total_stretch[fil]<>0 then o:=fil
> else o:=normal
10458,10464c12958,12966
< begin b←badness(x,total_stretch[normal]);
< if b>hbadness then
< begin print_ln;
< if b>100 then print_nl("Underfull")@+else print_nl("Loose");
< print(" \hbox (badness "); print_int(b);
< goto common_ending;
< end;
---
> begin last_badness:=badness(x,total_stretch[normal]);
> if last_badness>hbadness then
> begin print_ln;
> if last_badness>100 then print_nl("Underfull")@+else print_nl("Loose");
> print(" \hbox (badness "); print_int(last_badness);
> @.Underfull \\hbox...@>
> @.Loose \\hbox...@>
> goto common_ending;
> end;
10468,10469c12970,12972
< box originated, we use a global variable |par_begin_line| that is
< set nonzero only when |hpack| is being called by the paragraph builder.
---
> box originated, we use a global variable |pack_begin_line| that is
> set nonzero only when |hpack| is being called by the paragraph builder
> or the alignment finishing routine.
10472c12975,12976
< @!par_begin_line:integer; {source file line where the current paragraph began}
---
> @!pack_begin_line:integer; {source file line where the current paragraph
> or alignment began; a negative value denotes alignment}
10475c12979
< par_begin_line←0;
---
> pack_begin_line:=0;
10479,10485c12983,12991
< else begin if par_begin_line≠0 then
< begin print(") in paragraph at lines "); print_int(par_begin_line);
< print("--");
< end
< else print(") detected at line ");
< print_int(line);
< end;
---
> else begin if pack_begin_line<>0 then
> begin if pack_begin_line>0 then print(") in paragraph at lines ")
> else print(") in alignment at lines ");
> print_int(abs(pack_begin_line));
> print("--");
> end
> else print(") detected at line ");
> print_int(line);
> end;
10487,10488c12993,12994
< font_in_short_display←undefined_font; short_display(list_ptr(r)); print_ln;@/
< begin_diagnostic; show_box(r); end_diagnostic
---
> font_in_short_display:=null_font; short_display(list_ptr(r)); print_ln;@/
> begin_diagnostic; show_box(r); end_diagnostic(true)
10492,10502c12998,13012
< glue_order(r)←o; glue_sign(r)←shrinking;
< if total_shrink[o]=0 then glue_set(r)←0.0 {there's nothing to shrink}
< else glue_set(r)←-x/total_shrink[o];
< if (total_shrink[o]<-x)∧(o=normal)∧(list_ptr(r)≠null) then
< begin glue_set(r)←1.0; {this is the maximum shrinkage}
< @<Report an overfull hbox and |goto common_ending|, if this box
< is sufficiently bad@>;
< end
< else if (hbadness<100)∧(o=normal)∧(list_ptr(r)≠null) then
< @<Report a tight hbox and |goto common_ending|, if this box
< is sufficiently bad@>;
---
> glue_order(r):=o; glue_sign(r):=shrinking;
> if total_shrink[o]<>0 then glue_set(r):=unfloat((-x)/total_shrink[o])
> @^real division@>
> else begin glue_sign(r):=normal;
> set_glue_ratio_zero(glue_set(r)); {there's nothing to shrink}
> end;
> if (total_shrink[o]<-x)and(o=normal)and(list_ptr(r)<>null) then
> begin last_badness:=1000000;
> set_glue_ratio_one(glue_set(r)); {use the maximum shrinkage}
> @<Report an overfull hbox and |goto common_ending|, if this box
> is sufficiently bad@>;
> end
> else if o=normal then if list_ptr(r)<>null then
> @<Report a tight hbox and |goto common_ending|, if this box
> is sufficiently bad@>;
10507,10510c13017,13020
< if total_shrink[filll]≠0 then o←filll
< else if total_shrink[fill]≠0 then o←fill
< else if total_shrink[fil]≠0 then o←fil
< else o←normal
---
> if total_shrink[filll]<>0 then o:=filll
> else if total_shrink[fill]<>0 then o:=fill
> else if total_shrink[fil]<>0 then o:=fil
> else o:=normal
10513,10522c13023,13033
< if -x-total_shrink[normal]>hfuzz then
< begin if overfull_rule>0 then
< begin while link(q)≠null do q←link(q);
< link(q)←new_rule;
< width(link(q))←overfull_rule;
< end;
< print_ln; print_nl("Overfull \hbox (");
< print_scaled(-x-total_shrink[normal]); print("pt too wide");
< goto common_ending;
< end
---
> if (-x-total_shrink[normal]>hfuzz)or(hbadness<100) then
> begin if (overfull_rule>0)and(-x-total_shrink[normal]>hfuzz) then
> begin while link(q)<>null do q:=link(q);
> link(q):=new_rule;
> width(link(q)):=overfull_rule;
> end;
> print_ln; print_nl("Overfull \hbox (");
> @.Overfull \\hbox...@>
> print_scaled(-x-total_shrink[normal]); print("pt too wide");
> goto common_ending;
> end
10525,10529c13036,13041
< begin b←badness(-x,total_shrink[normal]);
< if b>hbadness then
< begin print_ln; print_nl("Tight \hbox (badness "); print_int(b);
< goto common_ending;
< end;
---
> begin last_badness:=badness(-x,total_shrink[normal]);
> if last_badness>hbadness then
> begin print_ln; print_nl("Tight \hbox (badness "); print_int(last_badness);
> @.Tight \\hbox...@>
> goto common_ending;
> end;
10542c13054
< pointer;
---
> pointer;
10549,10556c13061,13067
< @!b:integer; {badness of the new box}
< begin r←get_node(box_node_size); type(r)←vlist_node;
< subtype(r)←min_quarterword; shift_amount(r)←0;
< list_ptr(r)←p;@/
< w←0; @<Clear dimensions to zero@>;
< while p≠null do @<Examine node |p| in the vlist, taking account of its effect
< on the dimensions of the new box; then advance |p| to the next node@>;
< width(r)←w;
---
> begin last_badness:=0; r:=get_node(box_node_size); type(r):=vlist_node;
> subtype(r):=min_quarterword; shift_amount(r):=0;
> list_ptr(r):=p;@/
> w:=0; @<Clear dimensions to zero@>;
> while p<>null do @<Examine node |p| in the vlist, taking account of its effect
> on the dimensions of the new box; then advance |p| to the next node@>;
> width(r):=w;
10558,10560c13069,13071
< begin x←x+d-l; depth(r)←l;
< end
< else depth(r)←d;
---
> begin x:=x+d-l; depth(r):=l;
> end
> else depth(r):=d;
10562c13073
< then |return| or |goto common_ending|@>;
---
> then |return| or |goto common_ending|@>;
10564,10565c13075,13076
< for an overfull or underfull vbox@>;
< exit: vpackage←r;
---
> for an overfull or underfull vbox@>;
> exit: vpackage:=r;
10570,10583c13081,13092
< @:confusion vpack}{\quad vpack@>
< else case type(p) of
< hlist_node,vlist_node,rule_node,unset_node:
< @<Incorporate box dimensions into the dimensions of
< the vbox that will contain@@it@>;
< whatsit_node:@<Incorporate a whatsit node into a vbox@>;
< glue_node: begin x←x+d; d←0;
< @<Incorporate glue into the totals@>;
< end;
< kern_node: begin x←x+d+width(p); d←0;
< end;
< othercases do_nothing
< endcases;
< p←link(p);
---
> @:this can't happen vpack}{\quad vpack@>
> else case type(p) of
> hlist_node,vlist_node,rule_node,unset_node:
> @<Incorporate box dimensions into the dimensions of
> the vbox that will contain~it@>;
> whatsit_node:@<Incorporate a whatsit node into a vbox@>;
> glue_node: @<Incorporate glue into the vertical totals@>;
> kern_node: begin x:=x+d+width(p); d:=0;
> end;
> othercases do_nothing
> endcases;
> p:=link(p);
10587,10589c13096,13109
< begin x←x+d+height(p); d←depth(p);
< if type(p)≥rule_node then s←0 @+else s←shift_amount(p);
< if width(p)+s>w then w←width(p)+s;
---
> begin x:=x+d+height(p); d:=depth(p);
> if type(p)>=rule_node then s:=0 @+else s:=shift_amount(p);
> if width(p)+s>w then w:=width(p)+s;
> end
>
> @ @<Incorporate glue into the vertical totals@>=
> begin x:=x+d; d:=0;@/
> g:=glue_ptr(p); x:=x+width(g);@/
> o:=stretch_order(g); total_stretch[o]:=total_stretch[o]+stretch(g);
> o:=shrink_order(g); total_shrink[o]:=total_shrink[o]+shrink(g);
> if subtype(p)>=a_leaders then
> begin g:=leader_ptr(p);
> if width(g)>w then w:=width(g);
> end;
10596,10597c13116,13117
< if m=additional then h←x+h;
< height(r)←h; x←h-x; {now |x| is the excess to be made up}
---
> if m=additional then h:=x+h;
> height(r):=h; x:=h-x; {now |x| is the excess to be made up}
10599,10601c13119,13122
< begin glue_sign(r)←normal; glue_order(r)←normal; glue_set(r)←0.0;
< return;
< end
---
> begin glue_sign(r):=normal; glue_order(r):=normal;
> set_glue_ratio_zero(glue_set(r));
> return;
> end
10603c13124
< or \hbox{|goto common_ending|}@>
---
> or \hbox{|goto common_ending|}@>
10605c13126
< or \hbox{|goto common_ending|}@>
---
> or \hbox{|goto common_ending|}@>
10609,10614c13130,13138
< glue_order(r)←o; glue_sign(r)←stretching;
< if total_stretch[o]=0 then glue_set(r)←0.0 {there's nothing to stretch}
< else glue_set(r)←x/total_stretch[o];
< if (vbadness<inf_bad)∧(o=normal)∧(list_ptr(r)≠null) then
< @<Report an underfull vbox and |goto common_ending|, if this box
< is sufficiently bad@>;
---
> glue_order(r):=o; glue_sign(r):=stretching;
> if total_stretch[o]<>0 then glue_set(r):=unfloat(x/total_stretch[o])
> @^real division@>
> else begin glue_sign(r):=normal;
> set_glue_ratio_zero(glue_set(r)); {there's nothing to stretch}
> end;
> if o=normal then if list_ptr(r)<>null then
> @<Report an underfull vbox and |goto common_ending|, if this box
> is sufficiently bad@>;
10619,10625c13143,13151
< begin b←badness(x,total_stretch[normal]);
< if b>vbadness then
< begin print_ln;
< if b>100 then print_nl("Underfull")@+else print_nl("Loose");
< print(" \vbox (badness "); print_int(b);
< goto common_ending;
< end;
---
> begin last_badness:=badness(x,total_stretch[normal]);
> if last_badness>vbadness then
> begin print_ln;
> if last_badness>100 then print_nl("Underfull")@+else print_nl("Loose");
> print(" \vbox (badness "); print_int(last_badness);
> @.Underfull \\vbox...@>
> @.Loose \\vbox...@>
> goto common_ending;
> end;
10630,10633c13156,13165
< else begin print(") detected at line "); print_int(line);
< print_ln;@/
< end;
< begin_diagnostic; show_box(r); end_diagnostic
---
> else begin if pack_begin_line<>0 then {it's actually negative}
> begin print(") in alignment at lines ");
> print_int(abs(pack_begin_line));
> print("--");
> end
> else print(") detected at line ");
> print_int(line);
> print_ln;@/
> end;
> begin_diagnostic; show_box(r); end_diagnostic(true)
10637,10647c13169,13183
< glue_order(r)←o; glue_sign(r)←shrinking;
< if total_shrink[o]=0 then glue_set(r)←0.0 {there's nothing to shrink}
< else glue_set(r)←-x/total_shrink[o];
< if (total_shrink[o]<-x)∧(o=normal)∧(list_ptr(r)≠null) then
< begin glue_set(r)←1.0; {this is the maximum shrinkage}
< @<Report an overfull vbox and |goto common_ending|, if this box
< is sufficiently bad@>;
< end
< else if (vbadness<100)∧(o=normal)∧(list_ptr(r)≠null) then
< @<Report a tight vbox and |goto common_ending|, if this box
< is sufficiently bad@>;
---
> glue_order(r):=o; glue_sign(r):=shrinking;
> if total_shrink[o]<>0 then glue_set(r):=unfloat((-x)/total_shrink[o])
> @^real division@>
> else begin glue_sign(r):=normal;
> set_glue_ratio_zero(glue_set(r)); {there's nothing to shrink}
> end;
> if (total_shrink[o]<-x)and(o=normal)and(list_ptr(r)<>null) then
> begin last_badness:=1000000;
> set_glue_ratio_one(glue_set(r)); {use the maximum shrinkage}
> @<Report an overfull vbox and |goto common_ending|, if this box
> is sufficiently bad@>;
> end
> else if o=normal then if list_ptr(r)<>null then
> @<Report a tight vbox and |goto common_ending|, if this box
> is sufficiently bad@>;
10652,10656c13188,13193
< if -x-total_shrink[normal]>vfuzz then
< begin print_ln; print_nl("Overfull \vbox (");
< print_scaled(-x-total_shrink[normal]); print("pt too high");
< goto common_ending;
< end
---
> if (-x-total_shrink[normal]>vfuzz)or(vbadness<100) then
> begin print_ln; print_nl("Overfull \vbox (");
> @.Overfull \\vbox...@>
> print_scaled(-x-total_shrink[normal]); print("pt too high");
> goto common_ending;
> end
10659,10663c13196,13201
< begin b←badness(-x,total_shrink[normal]);
< if b>vbadness then
< begin print_ln; print_nl("Tight \vbox (badness "); print_int(b);
< goto common_ending;
< end;
---
> begin last_badness:=badness(-x,total_shrink[normal]);
> if last_badness>vbadness then
> begin print_ln; print_nl("Tight \vbox (badness "); print_int(last_badness);
> @.Tight \\vbox...@>
> goto common_ending;
> end;
10671,10680c13209,13218
< @!p:pointer; {a new glue specification}
< begin if prev_depth≠ignore_depth then
< begin d←width(baseline_skip)-prev_depth-height(b);
< if d<line_skip_limit then p←new_param_glue(line_skip_code)
< else begin p←new_skip_param(baseline_skip_code);
< width(temp_ptr)←d; {|temp_ptr=glue_ptr(p)|}
< end;
< link(tail)←p; tail←p;
< end;
< link(tail)←b; tail←b; prev_depth←depth(b);
---
> @!p:pointer; {a new glue node}
> begin if prev_depth>ignore_depth then
> begin d:=width(baseline_skip)-prev_depth-height(b);
> if d<line_skip_limit then p:=new_param_glue(line_skip_code)
> else begin p:=new_skip_param(baseline_skip_code);
> width(temp_ptr):=d; {|temp_ptr=glue_ptr(p)|}
> end;
> link(tail):=p; tail:=p;
> end;
> link(tail):=b; tail:=b; prev_depth:=depth(b);
10682c13220,13221
< @* \[33] Data structures for math mode.
---
>
> @* \[34] Data structures for math mode.
10697c13236
< classi\-fied as a relation, a binary operator, an open parenthesis, etc.,
---
> classified as a relation, a binary operator, an open parenthesis, etc.,
10715c13254
< nodes, math nodes, ligature nodes, mark nodes, insert nodes, adjust nodes,
---
> nodes, math nodes, ligature nodes,
10721c13260
< second, third, and fourth words are called the noad's |operand|, |subscr|,
---
> second, third, and fourth words are called the noad's |nucleus|, |subscr|,
10724c13263
< Consider, for example, the simple formula `\.{\$x\UA2\$}', which would be
---
> Consider, for example, the simple formula `\.{\$x\^2\$}', which would be
10726c13265
< The |operand| of this noad is a representation of `\.x', the |subscr| is
---
> The |nucleus| of this noad is a representation of `\.x', the |subscr| is
10729c13268
< The |operand|, |subscr|, and |supscr| fields are further broken into
---
> The |nucleus|, |subscr|, and |supscr| fields are further broken into
10737a13277,13282
> \yskip\hang|math_type(q)=math_text_char| is similar, but the character is
> unsubscripted and unsuperscripted and it is followed immediately by another
> character from the same font. (This |math_type| setting appears only
> briefly during the processing; it is used to suppress unwanted italic
> corrections.)
>
10751c13296
< is not the same as |math_type(q)=empty|; for example, `\.{\$P\DA\{\}\$}'
---
> is not the same as |math_type(q)=empty|; for example, `\.{\$P\_\{\}\$}'
10757c13302
< since a halfword is being used for the |math_type| although only two
---
> since a halfword is being used for the |math_type| although only three
10764c13309
< @d operand(#)==#+1 {the |operand| field of a noad}
---
> @d nucleus(#)==#+1 {the |nucleus| field of a noad}
10769d13313
< @d empty=0 {|math_type| when the attribute is absent}
10772a13317
> @d math_text_char=4 {|math_type| when italic correction is dubious}
10774,10775c13319,13320
< @ Each portion of a formula is classified as Ord, Op, Bin, Rel, Open,
< Close, Punct, or Inner, for purposes of spacing and line breaking. An
---
> @ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope,
> Clo, Pun, or Inn, for purposes of spacing and line breaking. An
10779c13324
< |rel_noad| whose |operand| field is a representation of an equals sign
---
> |rel_noad| whose |nucleus| field is a representation of an equals sign
10788c13333
< A noad of type |ord_noad|, |op_noad|, $\ldotss$, |inner_noad| usually
---
> A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually
10790,10791c13335,13336
< have |subtype=switched|, if \.{\\limitswitch} has been applied to this
< operator.
---
> have |subtype=limits| or |no_limits|, if the normal positioning of
> limits has been overridden for this operator.
10793c13338
< @d ord_noad=unset_node+2 {|type| of a noad classified Ord}
---
> @d ord_noad=unset_node+3 {|type| of a noad classified Ord}
10797,10801c13342,13347
< @d open_noad=ord_noad+4 {|type| of a noad classified Open}
< @d close_noad=ord_noad+5 {|type| of a noad classified Close}
< @d punct_noad=ord_noad+6 {|type| of a noad classified Punct}
< @d inner_noad=ord_noad+7 {|type| of a noad classified Inner}
< @d switched=1 {|subtype| of a limit-switched |op_noad|}
---
> @d open_noad=ord_noad+4 {|type| of a noad classified Ope}
> @d close_noad=ord_noad+5 {|type| of a noad classified Clo}
> @d punct_noad=ord_noad+6 {|type| of a noad classified Pun}
> @d inner_noad=ord_noad+7 {|type| of a noad classified Inn}
> @d limits=1 {|subtype| of |op_noad| whose scripts are to be above, below}
> @d no_limits=2 {|subtype| of |op_noad| whose scripts are to be normal}
10812c13358,13359
< starting characters, as explained in the manual.
---
> starting characters, as explained in Chapter~17 of {\sl The \TeX book}.
> @:TeXbook}{\sl The \TeX book@>
10816c13363
< |numerator| fields instead of |operand|, |subscr|, and |supscr|. The
---
> |numerator| fields instead of |nucleus|, |subscr|, and |supscr|. The
10825c13372,13373
< \.{\\atop}, \.{\\above}, \.{\\xoverx}, \.{\\xatopx}, and \.{\\xabovex}.
---
> \.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and
> \.{\\abovewithdelims}.
10844,10847c13392
< of delimiter fields; we can use the fact that a null delimiter is the same
< as an empty word of character information.
<
< @d null_delimiter==null_character
---
> of delimiter fields.
10850a13396
> @!null_delimiter:four_quarters;
10853c13399,13401
< empty_field.rh←empty; empty_field.lh←null;
---
> empty_field.rh:=empty; empty_field.lh:=null;@/
> null_delimiter.b0:=0; null_delimiter.b1:=min_quarterword;@/
> null_delimiter.b2:=0; null_delimiter.b3:=min_quarterword;
10855c13403
< @ The |new_noad| function creates an |inner_noad| that is completely null.
---
> @ The |new_noad| function creates an |ord_noad| that is completely null.
10859,10864c13407,13412
< begin p←get_node(noad_size);
< type(p)←inner_noad; subtype(p)←normal;
< mem[operand(p)].hh←empty_field;
< mem[subscr(p)].hh←empty_field;
< mem[supscr(p)].hh←empty_field;
< new_noad←p;
---
> begin p:=get_node(noad_size);
> type(p):=ord_noad; subtype(p):=normal;
> mem[nucleus(p)].hh:=empty_field;
> mem[subscr(p)].hh:=empty_field;
> mem[supscr(p)].hh:=empty_field;
> new_noad:=p;
10868,10869c13416,13417
< operand underlined; an |over_noad| has it overlined. An |accent_noad| places
< an accent over its operand; the accent character appears as
---
> nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places
> an accent over its nucleus; the accent character appears as
10871,10872c13419,13420
< centers its operand vertically with respect to the axis of the formula;
< we always have |math_type(operand(p))=sub_box| in this case.
---
> centers its nucleus vertically with respect to the axis of the formula;
> in such noads we always have |math_type(nucleus(p))=sub_box|.
10875c13423
< \TeX's \.{\\left} and \.{\\right}. The |operand| of such noads is
---
> \TeX's \.{\\left} and \.{\\right}. The |nucleus| of such noads is
10892c13440,13441
< @d delimiter==operand {|delimiter| field in left and right noads}
---
> @d delimiter==nucleus {|delimiter| field in left and right noads}
> @d scripts_allowed(#)==(type(#)>=ord_noad)and(type(#)<left_noad)
10896c13445
< data structure to record such instructions; it is two words long, so it
---
> data structure to record such instructions; it is three words long, so it
10899c13448,13449
< second word of a |style_node| is not used.
---
> second and third words of a |style_node| are not used, but they are
> present because a |choice_node| is converted to a |style_node|.
10902,10903c13452,13456
< |display_style|, $\ldotss$, |script_script_style|, and adds@@1 to get the
< ``cramped'' versions of these styles.
---
> |display_style|, \dots, |script_script_style|, and adds~1 to get the
> ``cramped'' versions of these styles. This gives a numerical order that
> is backwards from the convention of Appendix~G in {\sl The \TeX book\/};
> i.e., a smaller style has a larger numerical value.
> @:TeXbook}{\sl The \TeX book@>
10905a13459
> @d style_node_size=3 {number of words in a style node}
10914,10916c13468,13489
< begin p←get_node(small_node_size); type(p)←style_node;
< subtype(p)←s; width(p)←0; {the |width| is not used}
< new_style←p;
---
> begin p:=get_node(style_node_size); type(p):=style_node;
> subtype(p):=s; width(p):=0; depth(p):=0; {the |width| and |depth| are not used}
> new_style:=p;
> end;
>
> @ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which
> has special subfields |display_mlist|, |text_mlist|, |script_mlist|,
> and |script_script_mlist| pointing to the mlists for each style.
>
> @d choice_node=unset_node+2 {|type| of a choice node}
> @d display_mlist(#)==info(#+1) {mlist to be used in display style}
> @d text_mlist(#)==link(#+1) {mlist to be used in text style}
> @d script_mlist(#)==info(#+2) {mlist to be used in script style}
> @d script_script_mlist(#)==link(#+2) {mlist to be used in scriptscript style}
>
> @p function new_choice:pointer; {create a choice node}
> var p:pointer; {the new node}
> begin p:=get_node(style_node_size); type(p):=choice_node;
> subtype(p):=0; {the |subtype| is not used}
> display_mlist(p):=null; text_mlist(p):=null; script_mlist(p):=null;
> script_script_mlist(p):=null;
> new_choice:=p;
10920c13493
< that displays the things that can only be present in mlists; this
---
> that displays the things that can only be present in mlists; this
10927,10928c13500,13501
< subsidiary to the |operand| field of some noad; the dot is replaced by
< `\.[' or `\.(' or `\./' or `\.\\' if |p| is descended from the |subscr|
---
> subsidiary to the |nucleus| field of some noad; the dot is replaced by
> `\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr|
10930,10932c13503,13505
< the current string would be `\.{.(.[/}' if |p| points to the |ord_noad| for
< |x| in the (ridiculous) formula `\.{\$\\sqrt\{a\UA\{\{b\DA\{c\\over x+y\}
< \}\}\}\$}'.
---
> the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for
> |x| in the (ridiculous) formula
> `\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'.
10936,10938c13509,13512
< ord_noad,op_noad,bin_noad,rel_noad,open_noad,close_noad,punct_noad,
< radical_noad,over_noad,under_noad,vcenter_noad,accent_noad,
< left_noad,right_noad:@<Display normal noad |p|@>;
---
> choice_node:@<Display choice node |p|@>;
> ord_noad,op_noad,bin_noad,rel_noad,open_noad,close_noad,punct_noad,inner_noad,
> radical_noad,over_noad,under_noad,vcenter_noad,accent_noad,
> left_noad,right_noad:@<Display normal noad |p|@>;
10946c13520
< print_ascii(qo(character(p)));
---
> print_ASCII(qo(character(p)));
10949c13523
< procedure print_delimiter(@!p:pointer); {prints a delimiter as 24-bit octal}
---
> procedure print_delimiter(@!p:pointer); {prints a delimiter as 24-bit hex value}
10951,10952c13525,13526
< begin a←small_fam(p)*256+qo(small_char(p));
< a←a*@'10000+large_fam(p)*256+qo(large_char(p));
---
> begin a:=small_fam(p)*256+qo(small_char(p));
> a:=a*@"1000+large_fam(p)*256+qo(large_char(p));
10954c13528
< else print_octal(a);
---
> else print_hex(a);
10966,10982c13540,13558
< procedure print_subsidiary_data(@!p:pointer;@!c:ascii_code);
< {display a noad field}
< begin if cur_length<depth_threshold then
< begin append_char(c); {include |c| in the recursion history}
< temp_ptr←p; {prepare for |show_info| if recursion is needed}
< case math_type(p) of
< math_char: begin print_ln; print_current_string; print_fam_and_char(p);
< end;
< sub_box: show_info; {recursive call}
< sub_mlist: if info(p)=null then
< begin print_ln; print_current_string; print("{}");
< end
< else show_info; {recursive call}
< othercases do_nothing {|empty|}
< endcases;@/
< flush_char; {remove |c| from the recursion history}
< end;
---
> procedure print_subsidiary_data(@!p:pointer;@!c:ASCII_code);
> {display a noad field}
> begin if cur_length>=depth_threshold then
> begin if math_type(p)<>empty then print(" []");
> end
> else begin append_char(c); {include |c| in the recursion history}
> temp_ptr:=p; {prepare for |show_info| if recursion is needed}
> case math_type(p) of
> math_char: begin print_ln; print_current_string; print_fam_and_char(p);
> end;
> sub_box: show_info; {recursive call}
> sub_mlist: if info(p)=null then
> begin print_ln; print_current_string; print("{}");
> end
> else show_info; {recursive call}
> othercases do_nothing {|empty|}
> endcases;@/
> flush_char; {remove |c| from the recursion history}
> end;
11009a13586,13593
> @ @<Display choice node |p|@>=
> begin print_esc("mathchoice");
> append_char("D"); show_node_list(display_mlist(p)); flush_char;
> append_char("T"); show_node_list(text_mlist(p)); flush_char;
> append_char("S"); show_node_list(script_mlist(p)); flush_char;
> append_char("s"); show_node_list(script_script_mlist(p)); flush_char;
> end
>
11018a13603
> inner_noad: print_esc("mathinner");
11023c13608
< end;
---
> end;
11025,11034c13610,13621
< end;
< left_noad: begin print_esc("left"); print_delimiter(operand(p));
< end;
< right_noad: begin print_esc("right"); print_delimiter(operand(p));
< end;
< end;
< if subtype(p)≠normal then print_esc("limitswitch");
< if type(p)<left_noad then print_subsidiary_data(operand(p),".");
< print_subsidiary_data(supscr(p),"(");
< print_subsidiary_data(subscr(p),"[");
---
> end;
> left_noad: begin print_esc("left"); print_delimiter(delimiter(p));
> end;
> right_noad: begin print_esc("right"); print_delimiter(delimiter(p));
> end;
> end;
> if subtype(p)<>normal then
> if subtype(p)=limits then print_esc("limits")
> else print_esc("nolimits");
> if type(p)<left_noad then print_subsidiary_data(nucleus(p),".");
> print_subsidiary_data(supscr(p),"^");
> print_subsidiary_data(subscr(p),"_");
11038c13625
< begin print_esc("xabovex"); print(" thickness ");
---
> begin print_esc("fraction, thickness ");
11041,11052c13628,13639
< if (small_fam(left_delimiter(p))≠0)∨@+
< (small_char(left_delimiter(p))≠min_quarterword)∨@|
< (large_fam(left_delimiter(p))≠0)∨@|
< (large_char(left_delimiter(p))≠min_quarterword) then
< begin print(", left-delimiter "); print_delimiter(left_delimiter(p));
< end;
< if (small_fam(right_delimiter(p))≠0)∨@|
< (small_char(right_delimiter(p))≠min_quarterword)∨@|
< (large_fam(right_delimiter(p))≠0)∨@|
< (large_char(right_delimiter(p))≠min_quarterword) then
< begin print(", right-delimiter "); print_delimiter(right_delimiter(p));
< end;
---
> if (small_fam(left_delimiter(p))<>0)or@+
> (small_char(left_delimiter(p))<>min_quarterword)or@|
> (large_fam(left_delimiter(p))<>0)or@|
> (large_char(left_delimiter(p))<>min_quarterword) then
> begin print(", left-delimiter "); print_delimiter(left_delimiter(p));
> end;
> if (small_fam(right_delimiter(p))<>0)or@|
> (small_char(right_delimiter(p))<>min_quarterword)or@|
> (large_fam(right_delimiter(p))<>0)or@|
> (large_char(right_delimiter(p))<>min_quarterword) then
> begin print(", right-delimiter "); print_delimiter(right_delimiter(p));
> end;
11056c13643,13675
< @* \[34] Subroutines for math mode.
---
>
> @ That which can be displayed can also be destroyed.
>
> @<Cases of |flush_node_list| that arise...@>=
> style_node: begin free_node(p,style_node_size); goto done;
> end;
> choice_node:begin flush_node_list(display_mlist(p));
> flush_node_list(text_mlist(p));
> flush_node_list(script_mlist(p));
> flush_node_list(script_script_mlist(p));
> free_node(p,style_node_size); goto done;
> end;
> ord_noad,op_noad,bin_noad,rel_noad,open_noad,close_noad,punct_noad,inner_noad,
> radical_noad,over_noad,under_noad,vcenter_noad,accent_noad:@t@>@;@/
> begin if math_type(nucleus(p))>=sub_box then
> flush_node_list(info(nucleus(p)));
> if math_type(supscr(p))>=sub_box then
> flush_node_list(info(supscr(p)));
> if math_type(subscr(p))>=sub_box then
> flush_node_list(info(subscr(p)));
> if type(p)=radical_noad then free_node(p,radical_noad_size)
> else if type(p)=accent_noad then free_node(p,accent_noad_size)
> else free_node(p,noad_size);
> goto done;
> end;
> left_noad,right_noad: begin free_node(p,noad_size); goto done;
> end;
> fraction_noad: begin flush_node_list(info(numerator(p)));
> flush_node_list(info(denominator(p)));
> free_node(p,fraction_noad_size); goto done;
> end;
>
> @* \[35] Subroutines for math mode.
11072c13691
< begin if s=0 then print_esc("textfont")
---
> begin if s=text_size then print_esc("textfont")
11077,11080c13696,13699
< @ Before an mlist is created, \TeX\ makes sure that
< the fonts in family@@2 have enough parameters to be math-symbol
< fonts, and that the text font in family@@3 has enough parameters to be a
< math-extension font. The math-symbol parameters are referred to by using the
---
> @ Before an mlist is converted to an hlist, \TeX\ makes sure that
> the fonts in family~2 have enough parameters to be math-symbol
> fonts, and that the fonts in family~3 have enough parameters to be
> math-extension fonts. The math-symbol parameters are referred to by using the
11102,11103c13721,13723
< @d delim1==mathsy(20) {size of \.{\\xatopx} delimiters in display styles}
< @d delim2==mathsy(21) {size of \.{\\xatopx} delimiters in non-displays}
---
> @d delim1==mathsy(20) {size of \.{\\atopwithdelims} delimiters
> in display styles}
> @d delim2==mathsy(21) {size of \.{\\atopwithdelims} delimiters in non-displays}
11123c13743
< an overlined operand (|und_style|), for a subscript or a superscript
---
> an overlined nucleus (|cramped_style|), for a subscript or a superscript
11127c13747
< @d und_style(#)==2*(# div 2)+cramped {cramp the style}
---
> @d cramped_style(#)==2*(# div 2)+cramped {cramp the style}
11137,11139c13757,13759
< begin if cur_style<script_style then cur_size←text_size
< else cur_size←16*((cur_style-text_style) div 2);
< cur_mu←x_over_n(math_quad(cur_style),18);
---
> begin if cur_style<script_style then cur_size:=text_size
> else cur_size:=16*((cur_style-text_style) div 2);
> cur_mu:=x_over_n(math_quad(cur_size),18);
11147c13767
< {construct the bar for a fraction}
---
> {construct the bar for a fraction}
11149c13769
< begin p←new_rule; height(p)←t; depth(p)←0; fraction_rule←p;
---
> begin p:=new_rule; height(p):=t; depth(p):=0; fraction_rule:=p;
11158,11159c13778,13779
< begin p←new_kern(k); link(p)←b; q←fraction_rule(t); link(q)←p;
< p←new_kern(t); link(p)←q; overbar←vpack(p,natural);
---
> begin p:=new_kern(k); link(p):=b; q:=fraction_rule(t); link(q):=p;
> p:=new_kern(t); link(p):=q; overbar:=vpack(p,natural);
11189d13808
< @!p: pointer; {character nodes constructed}
11192,11206c13811,13825
< begin f←undefined_font; w←0; large_attempt←false;
< z←small_fam(d); x←small_char(d);
< loop@+ begin @<Look at the variants of |(z,x)|; set |f| and |c| whenever
< a better character is found; |goto found| as soon as a
< large enough variant is encountered@>;
< if large_attempt then goto found; {there were none large enough}
< large_attempt←true; z←large_fam(d); x←large_char(d);
< end;
< found: if f≠undefined_font then
< @<Make variable |b| point to a box for |(f,c)|@>
< else begin b←new_null_box;
< width(b)←null_delimiter_space; {use this width if no delimiter was found}
< end;
< shift_amount(b)←half(height(b)-depth(b)) - axis_height(s);
< var_delimiter←b;
---
> begin f:=null_font; w:=0; large_attempt:=false;
> z:=small_fam(d); x:=small_char(d);
> loop@+ begin @<Look at the variants of |(z,x)|; set |f| and |c| whenever
> a better character is found; |goto found| as soon as a
> large enough variant is encountered@>;
> if large_attempt then goto found; {there were none large enough}
> large_attempt:=true; z:=large_fam(d); x:=large_char(d);
> end;
> found: if f<>null_font then
> @<Make variable |b| point to a box for |(f,c)|@>
> else begin b:=new_null_box;
> width(b):=null_delimiter_space; {use this width if no delimiter was found}
> end;
> shift_amount(b):=half(height(b)-depth(b)) - axis_height(s);
> var_delimiter:=b;
11214,11223c13833,13842
< if (z≠0)∨(x≠min_quarterword) then
< begin z←z+s+16;
< repeat z←z-16; g←fam_fnt(z);
< if g≠undefined_font then
< @<Look at the list of characters starting with |x| in
< font |g|; set |f| and |c| whenever
< a better character is found; |goto found| as soon as a
< large enough variant is encountered@>;
< until z<16;
< end
---
> if (z<>0)or(x<>min_quarterword) then
> begin z:=z+s+16;
> repeat z:=z-16; g:=fam_fnt(z);
> if g<>null_font then
> @<Look at the list of characters starting with |x| in
> font |g|; set |f| and |c| whenever
> a better character is found; |goto found| as soon as a
> large enough variant is encountered@>;
> until z<16;
> end
11226,11242c13845,13880
< begin y←x;
< continue: if (qo(y)≥font_bc[g])∧(qo(y)≤font_ec[g]) then
< begin q←char_info(g)(y);
< if char_exists(q) then
< begin if char_tag(q)=ext_tag then goto found;
< hd←height_depth(q);
< u←char_height(g)(hd)+char_depth(g)(hd);
< if u>w then
< begin f←g; c←y; w←u;
< if u≥v then goto found;
< end;
< if char_tag(q)=list_tag then
< begin y←rem_byte(q); goto continue;
< end;
< end;
< end;
< end
---
> begin y:=x;
> if (qo(y)>=font_bc[g])and(qo(y)<=font_ec[g]) then
> begin continue: q:=char_info(g)(y);
> if char_exists(q) then
> begin if char_tag(q)=ext_tag then
> begin f:=g; c:=y; goto found;
> end;
> hd:=height_depth(q);
> u:=char_height(g)(hd)+char_depth(g)(hd);
> if u>w then
> begin f:=g; c:=y; w:=u;
> if u>=v then goto found;
> end;
> if char_tag(q)=list_tag then
> begin y:=rem_byte(q); goto continue;
> end;
> end;
> end;
> end
>
> @ Here is a subroutine that creates a new box, whose list contains a
> single character, and whose width includes the italic correction for
> that character. The height or depth of the box will be negative, if
> the height or depth of the character is negative; thus, this routine
> may deliver a slightly different result than |hpack| would produce.
>
> @<Declare subprocedures for |var_delimiter|@>=
> function char_box(@!f:internal_font_number;@!c:quarterword):pointer;
> var q:four_quarters;
> @!hd:eight_bits; {|height_depth| byte}
> @!b,@!p:pointer; {the new box and its character node}
> begin q:=char_info(f)(c); hd:=height_depth(q);
> b:=new_null_box; width(b):=char_width(f)(q)+char_italic(f)(q);
> height(b):=char_height(f)(hd); depth(b):=char_depth(f)(hd);
> p:=get_avail; character(p):=c; font(p):=f; list_ptr(b):=p; char_box:=b;
> end;
11249,11252c13887,13889
< @<Construct an extensible character for |(f,c)| in box |b|@>
< else begin p←get_avail; font(p)←f; character(p)←c; list_ptr(b)←p;
< b←hpack(p,natural);
< end
---
> @<Construct an extensible character in a new box |b|,
> using recipe |rem_byte(q)| and font |f|@>
> else b:=char_box(f,c)
11260c13897
< @!c:quarterword);
---
> @!c:quarterword);
11262,11264c13899,13900
< begin p←get_avail; font(p)←f; character(p)←c; p←hpack(p,natural);
< link(p)←list_ptr(b); list_ptr(b)←p;
< height(b)←height(p);
---
> begin p:=char_box(f,c); link(p):=list_ptr(b); list_ptr(b):=p;
> height(b):=height(p);
11274,11275c13910,13911
< begin q←char_info(f)(c); hd←height_depth(q);
< height_plus_depth←char_height(f)(hd)+char_depth(f)(hd);
---
> begin q:=char_info(f)(c); hd:=height_depth(q);
> height_plus_depth:=char_height(f)(hd)+char_depth(f)(hd);
11279,11281c13915,13917
< begin b←new_null_box;
< type(b)←vlist_node;
< r←font_info[exten_base[f]+rem_byte(q)].qqqq;@/
---
> begin b:=new_null_box;
> type(b):=vlist_node;
> r:=font_info[exten_base[f]+rem_byte(q)].qqqq;@/
11283,11295c13919,13931
< number of extension steps, |n|; also set |width(b)|@>;
< c←ext_bot(r);
< if c≠min_quarterword then stack_into_box(b,f,c);
< c←ext_rep(r);
< for m←1 to n do stack_into_box(b,f,c);
< c←ext_mid(r);
< if c≠min_quarterword then
< begin stack_into_box(b,f,c); c←ext_rep(r);
< for m←1 to n do stack_into_box(b,f,c);
< end;
< c←ext_top(r);
< if c≠min_quarterword then stack_into_box(b,f,c);
< depth(b)←w-height(b);
---
> number of extension steps, |n|; also set |width(b)|@>;
> c:=ext_bot(r);
> if c<>min_quarterword then stack_into_box(b,f,c);
> c:=ext_rep(r);
> for m:=1 to n do stack_into_box(b,f,c);
> c:=ext_mid(r);
> if c<>min_quarterword then
> begin stack_into_box(b,f,c); c:=ext_rep(r);
> for m:=1 to n do stack_into_box(b,f,c);
> end;
> c:=ext_top(r);
> if c<>min_quarterword then stack_into_box(b,f,c);
> depth(b):=w-height(b);
11299c13935
< mod\-ule. If this module does not have positive height plus depth,
---
> module. If this module does not have positive height plus depth,
11304,11309c13940,13945
< c←ext_rep(r); u←height_plus_depth(f,c);
< w←0; width(b)←char_width(f)(char_info(f)(c));@/
< c←ext_bot(r);@+if c≠min_quarterword then w←w+height_plus_depth(f,c);
< c←ext_mid(r);@+if c≠min_quarterword then w←w+height_plus_depth(f,c);
< c←ext_top(r);@+if c≠min_quarterword then w←w+height_plus_depth(f,c);
< n←0;
---
> c:=ext_rep(r); u:=height_plus_depth(f,c);
> w:=0; q:=char_info(f)(c); width(b):=char_width(f)(q)+char_italic(f)(q);@/
> c:=ext_bot(r);@+if c<>min_quarterword then w:=w+height_plus_depth(f,c);
> c:=ext_mid(r);@+if c<>min_quarterword then w:=w+height_plus_depth(f,c);
> c:=ext_top(r);@+if c<>min_quarterword then w:=w+height_plus_depth(f,c);
> n:=0;
11311,11313c13947,13949
< begin w←w+u; incr(n);
< if ext_mid(r)≠min_quarterword then w←w+u;
< end
---
> begin w:=w+u; incr(n);
> if ext_mid(r)<>min_quarterword then w:=w+u;
> end
11317,11318c13953,13954
< their limits above and below. It takes a given hlist box |b| and
< changes it so that the new box is centered in a box of width |w|.
---
> their limits above and below. It takes a given box~|b| and
> changes it so that the new box is centered in a box of width~|w|.
11323a13960,13963
> The given box might contain a single character whose italic correction
> has been added to the width of the box; in this case a compensating
> kern is inserted.
>
11326,11334c13966,13982
< begin if (width(b)≠w)∧(list_ptr(b)≠null) then
< begin p←list_ptr(b); free_node(b,box_node_size);
< b←new_glue(ss_glue); link(b)←p;
< while link(p)≠null do p←link(p);
< link(p)←new_glue(ss_glue);
< rebox←hpack(b,w,exactly);
< end
< else begin width(b)←w; rebox←b;
< end;
---
> @!f:internal_font_number; {font in a one-character box}
> @!v:scaled; {width of a character without italic correction}
> begin if (width(b)<>w)and(list_ptr(b)<>null) then
> begin if type(b)=vlist_node then b:=hpack(b,natural);
> p:=list_ptr(b);
> if (is_char_node(p))and(link(p)=null) then
> begin f:=font(p); v:=char_width(f)(char_info(f)(character(p)));
> if v<>width(b) then link(p):=new_kern(width(b)-v);
> end;
> free_node(b,box_node_size);
> b:=new_glue(ss_glue); link(b):=p;
> while link(p)<>null do p:=link(p);
> link(p):=new_glue(ss_glue);
> rebox:=hpack(b,w,exactly);
> end
> else begin width(b):=w; rebox:=b;
> end;
11339a13988,13989
> @d mu_mult(#)==nx_plus_y(n,#,xn_over_d(#,f,@'200000))
>
11344,11353c13994,14006
< begin n←x_over_n(m,@'200000); f←remainder;@/
< p←get_node(glue_spec_size);
< width(p)←nx_plus_y(n,width(g),f); {convert \.{mu} to \.{pt}}
< stretch_order(p)←stretch_order(g);
< if stretch_order(p)=normal then stretch(p)←nx_plus_y(n,stretch(g),f)
< else stretch(p)←stretch(g);
< shrink_order(p)←shrink_order(g);
< if shrink_order(p)=normal then shrink(p)←nx_plus_y(n,shrink(g),f)
< else shrink(p)←shrink(g);
< math_glue←p;
---
> begin n:=x_over_n(m,@'200000); f:=remainder;@/
> if f<0 then
> begin decr(n); f:=f+@'200000;
> end;
> p:=get_node(glue_spec_size);
> width(p):=mu_mult(width(g)); {convert \.{mu} to \.{pt}}
> stretch_order(p):=stretch_order(g);
> if stretch_order(p)=normal then stretch(p):=mu_mult(stretch(g))
> else stretch(p):=stretch(g);
> shrink_order(p):=shrink_order(g);
> if shrink_order(p)=normal then shrink(p):=mu_mult(shrink(g))
> else shrink(p):=shrink(g);
> math_glue:=p;
11363,11376c14016,14041
< begin n←x_over_n(m,@'200000); f←remainder;@/
< width(p)←nx_plus_y(n,width(p),f); subtype(p)←normal;
< end;
< end;
< @* \[35] Typesetting math formulas.
< \TeX's major routine for dealing with formulas is called |mlist_to_hlist|.
< After a formula has been scanned and represented as an mlist, this routine
< converts it to an hlist that can be placed into a box or incorporated into
< the text of a paragraph. There are three implicit parameters, passed in
< global variables: |cur_mlist| points to the first node or noad in the
< given mlist (and it might be |null|); |cur_style| is a style code; and
< |mlist_penalties| is |true| if penalty nodes for potential line breaks are
< to be inserted into the resulting hlist. After |mlist_to_hlist| has
< acted, |link(temp_head)| points to the translated hlist.
---
> begin n:=x_over_n(m,@'200000); f:=remainder;@/
> if f<0 then
> begin decr(n); f:=f+@'200000;
> end;
> width(p):=mu_mult(width(p)); subtype(p):=explicit;
> end;
> end;
>
> @ Sometimes it is necessary to destroy an mlist. The following
> subroutine empties the current list, assuming that |abs(mode)=mmode|.
>
> @p procedure flush_math;
> begin flush_node_list(link(head)); flush_node_list(incompleat_noad);
> link(head):=null; tail:=head; incompleat_noad:=null;
> end;
>
> @* \[36] Typesetting math formulas.
> \TeX's most important routine for dealing with formulas is called
> |mlist_to_hlist|. After a formula has been scanned and represented as an
> mlist, this routine converts it to an hlist that can be placed into a box
> or incorporated into the text of a paragraph. There are three implicit
> parameters, passed in global variables: |cur_mlist| points to the first
> node or noad in the given mlist (and it might be |null|); |cur_style| is a
> style code; and |mlist_penalties| is |true| if penalty nodes for potential
> line breaks are to be inserted into the resulting hlist. After
> |mlist_to_hlist| has acted, |link(temp_head)| points to the translated hlist.
11393c14058,14061
< |mlist_to_hlist|. The box returned by |clean_box| is ``clean'' in the
---
> |mlist_to_hlist|.
> @^recursion@>
>
> The box returned by |clean_box| is ``clean'' in the
11396c14064
< @p procedure mlist_to_hlist; forward;@t\2@>@/
---
> @p procedure@?mlist_to_hlist; forward;@t\2@>@/
11398c14066
< label found,exit;
---
> label found;
11400a14069,14070
> @!x:pointer; {box to be returned}
> @!r:pointer; {temporary pointer}
11402,11413c14072,14078
< math_char: begin cur_mlist←new_noad; mem[operand(cur_mlist)]←mem[p];
< type(cur_mlist)←ord_noad;
< end;
< sub_box: begin q←info(p);
< if shift_amount(q)=0 then {already clean}
< begin clean_box←q; return;
< end
< else goto found;
< end;
< sub_mlist: cur_mlist←info(p);
< othercases begin q←null; goto found;
< end
---
> math_char: begin cur_mlist:=new_noad; mem[nucleus(cur_mlist)]:=mem[p];
> end;
> sub_box: begin q:=info(p); goto found;
> end;
> sub_mlist: cur_mlist:=info(p);
> othercases begin q:=new_null_box; goto found;
> end
11415,11417c14080,14082
< save_style←cur_style; cur_style←s; mlist_penalties←false;
< mlist_to_hlist; q←link(temp_head); {recursive call}
< cur_style←save_style; {restore the style}
---
> save_style:=cur_style; cur_style:=s; mlist_penalties:=false;@/
> mlist_to_hlist; q:=link(temp_head); {recursive call}
> cur_style:=save_style; {restore the style}
11419,11420c14084,14102
< found: clean_box←hpack(q,natural);
< exit:end;
---
> found: if is_char_node(q)or(q=null) then x:=hpack(q,natural)
> else if (link(q)=null)and(type(q)<=vlist_node)and(shift_amount(q)=0) then
> x:=q {it's already clean}
> else x:=hpack(q,natural);
> @<Simplify a trivial box@>;
> clean_box:=x;
> end;
>
> @ Here we save memory space in a common case.
>
> @<Simplify a trivial box@>=
> q:=list_ptr(x);
> if is_char_node(q) then
> begin r:=link(q);
> if r<>null then if link(r)=null then if not is_char_node(r) then
> if type(r)=kern_node then {unneeded italic correction}
> begin free_node(r,small_node_size); link(q):=null;
> end;
> end
11430,11440c14112,14122
< begin cur_c←character(a); cur_f←fam_fnt(fam(a)+cur_size);
< if cur_f=undefined_font then
< @<Complain about an undefined family and set |cur_i| null@>
< else begin if (qo(cur_c)≥font_bc[cur_f])∧(qo(cur_c)≤font_ec[cur_f]) then
< cur_i←char_info(cur_f)(cur_c)
< else cur_i←null_character;
< if not(char_exists(cur_i)) then
< begin char_warning(cur_f,cur_c);
< math_type(a)←empty;
< end;
< end;
---
> begin cur_c:=character(a); cur_f:=fam_fnt(fam(a)+cur_size);
> if cur_f=null_font then
> @<Complain about an undefined family and set |cur_i| null@>
> else begin if (qo(cur_c)>=font_bc[cur_f])and(qo(cur_c)<=font_ec[cur_f]) then
> cur_i:=char_info(cur_f)(cur_c)
> else cur_i:=null_character;
> if not(char_exists(cur_i)) then
> begin char_warning(cur_f,qo(cur_c));
> math_type(a):=empty;
> end;
> end;
11444,11452c14126,14133
< begin print_nl("! "); print_size(cur_size); print(" ");
< print_int(fam(a)); print(" is undefined");
< @:text_font_}{\.{\\textfont x is undefined}@>
< @:script_font_}{\.{\\scriptfont x is undefined}@>
< @:script_script_font_}{\.{\\scriptscriptfont x is undefined}@>
< help3("You have to define families outside of a math")@/
< ("subformula that uses them. Proceed, and I'll")@/
< ("ignore the nonexistent font you referred to.");
< error; cur_i←null_character; math_type(a)←empty;
---
> begin print_err(""); print_size(cur_size); print_char(" ");
> print_int(fam(a)); print(" is undefined (character ");
> print_ASCII(qo(cur_c)); print_char(")");
> help4("Somewhere in the math formula just ended, you used the")@/
> ("stated character from an undefined font family. For example,")@/
> ("plain TeX doesn't allow \it or \sl in subscripts. Proceed,")@/
> ("and I'll try to forget that I needed that character.");
> error; cur_i:=null_character; math_type(a):=empty;
11461c14142
< or a lig/kern instruction}
---
> or a lig/kern instruction}
11473c14154,14155
< field, an integer field that replaces the |operand| or |thickness|.
---
> field, an integer field that replaces the |nucleus| or |thickness|.
> @^recursion@>
11476c14158
< penal\-ties between nodes.
---
> penalties between nodes.
11478c14160
< @d new_hlist(#)==mem[operand(#)].int {the translation of an mlist}
---
> @d new_hlist(#)==mem[nucleus(#)].int {the translation of an mlist}
11491c14173
< done;
---
> done;
11500c14182
< @!p,@!v,@!x,@!y,@!z: pointer; {temporary registers for list construction}
---
> @!p,@!x,@!y,@!z: pointer; {temporary registers for list construction}
11505,11507c14187,14189
< begin mlist←cur_mlist; penalties←mlist_penalties;
< style←cur_style; {tuck global parameters away}
< q←mlist; r←null; r_type←op_noad; max_h←0; max_d←0;
---
> begin mlist:=cur_mlist; penalties:=mlist_penalties;
> style:=cur_style; {tuck global parameters away as local variables}
> q:=mlist; r:=null; r_type:=op_noad; max_h:=0; max_d:=0;
11509,11511c14191,14194
< while q≠null do @<Process node-or-noad |q| as much as possible in preparation
< for the second pass of |mlist_to_hlist|, then move to the next
< item in the mlist@>;
---
> while q<>null do @<Process node-or-noad |q| as much as possible in preparation
> for the second pass of |mlist_to_hlist|, then move to the next
> item in the mlist@>;
> @<Convert \(a)a final |bin_noad| to an |ord_noad|@>;
11513c14196
< proper spacing and penalties@>;
---
> proper spacing and penalties@>;
11516c14199
< @ We use the fact that no character nodes appear in an mlist, hence
---
> @ We use the fact that no character nodes appear in an mlist, hence
11521,11526c14204,14209
< if a noad has been fully processed, |goto check_dimensions| if it
< has been translated into |new_hlist(q)|, or |goto done_with_node|
< if a node has been fully processed@>;
< check_dimensions: z←hpack(new_hlist(q),natural);
< if height(z)>max_h then max_h←height(z);
< if depth(z)>max_d then max_d←depth(z);
---
> if a noad has been fully processed, |goto check_dimensions| if it
> has been translated into |new_hlist(q)|, or |goto done_with_node|
> if a node has been fully processed@>;
> check_dimensions: z:=hpack(new_hlist(q),natural);
> if height(z)>max_h then max_h:=height(z);
> if depth(z)>max_d then max_d:=depth(z);
11528,11529c14211,14212
< done_with_noad: r←q; r_type←type(r);
< done_with_node: q←link(q);
---
> done_with_noad: r:=q; r_type:=type(r);
> done_with_node: q:=link(q);
11537c14220
< reswitch: delta←0;
---
> reswitch: delta:=0;
11540,11544c14223,14227
< bin_noad,op_noad,rel_noad,open_noad,punct_noad,left_noad:
< begin type(q)←ord_noad; goto reswitch;
< end;
< othercases do_nothing
< endcases;
---
> bin_noad,op_noad,rel_noad,open_noad,punct_noad,left_noad:
> begin type(q):=ord_noad; goto reswitch;
> end;
> othercases do_nothing
> endcases;
11546,11548c14229,14231
< if r_type=bin_noad then type(r)←ord_noad;
< if type(q)=right_noad then goto done_with_noad;
< end;
---
> @<Convert \(a)a final |bin_noad| to an |ord_noad|@>;
> if type(q)=right_noad then goto done_with_noad;
> end;
11551c14234
< |goto done_with_node|@>@;
---
> |goto done_with_node|@>@;
11553,11555c14236,14241
< @:confusion mlist1}{\quad mlist1@>
< endcases;
< @<Convert \(o)|operand(q)| to an hlist and attach the sub/superscripts@>
---
> @:this can't happen mlist1}{\quad mlist1@>
> endcases;@/
> @<Convert \(n)|nucleus(q)| to an hlist and attach the sub/superscripts@>
>
> @ @<Convert \(a)a final |bin_noad| to an |ord_noad|@>=
> if r_type=bin_noad then type(r):=ord_noad
11558,11565c14244,14254
< style_node: begin cur_style←subtype(q);
< @<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
< goto done_with_node;
< end;
< whatsit_node,penalty_node,disc_node: goto done_with_node;
< rule_node: begin if height(q)>max_h then max_h←height(q);
< if depth(q)>max_d then max_d←depth(q); goto done_with_node;
< end;
---
> style_node: begin cur_style:=subtype(q);
> @<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
> goto done_with_node;
> end;
> choice_node: @<Change this node to a style node followed by the correct choice,
> then |goto done_with_node|@>;
> ins_node,mark_node,adjust_node,
> whatsit_node,penalty_node,disc_node: goto done_with_node;
> rule_node: begin if height(q)>max_h then max_h:=height(q);
> if depth(q)>max_d then max_d:=depth(q); goto done_with_node;
> end;
11567,11568c14256,14257
< goto done_with_node;
< end;
---
> goto done_with_node;
> end;
11570c14259,14281
< end;
---
> end;
>
> @ @d choose_mlist(#)==begin p:=#(q); #(q):=null;@+end
>
> @<Change this node to a style node...@>=
> begin case cur_style div 2 of
> 0: choose_mlist(display_mlist); {|display_style=0|}
> 1: choose_mlist(text_mlist); {|text_style=2|}
> 2: choose_mlist(script_mlist); {|script_style=4|}
> 3: choose_mlist(script_script_mlist); {|script_script_style=6|}
> end; {there are no other cases}
> flush_node_list(display_mlist(q));
> flush_node_list(text_mlist(q));
> flush_node_list(script_mlist(q));
> flush_node_list(script_script_mlist(q));@/
> type(q):=style_node; subtype(q):=cur_style; width(q):=0; depth(q):=0;
> if p<>null then
> begin z:=link(q); link(q):=p;
> while link(p)<>null do p:=link(p);
> link(p):=z;
> end;
> goto done_with_node;
> end
11576c14287
< (`\.{\\mskip}') is converted to normal glue by multiplying the dimensions
---
> (`\.{\\muskip}') is converted to normal glue by multiplying the dimensions
11577a14289
> @!@:non_script_}{\.{\\nonscript} primitive@>
11581,11589c14293,14302
< begin x←glue_ptr(q);
< y←math_glue(x,cur_mu); delete_glue_ref(x); glue_ptr(q)←y;
< end
< else if (cur_size>text_size)∧(subtype(q)=cond_math_glue) then
< begin p←link(q);
< if p≠null then if (type(p)=glue_node)∨(type(p)=kern_node) then
< begin link(q)←link(p); link(p)←null; flush_node_list(p);
< end;
< end
---
> begin x:=glue_ptr(q);
> y:=math_glue(x,cur_mu); delete_glue_ref(x); glue_ptr(q):=y;
> subtype(q):=normal;
> end
> else if (cur_size<>text_size)and(subtype(q)=cond_math_glue) then
> begin p:=link(q);
> if p<>null then if (type(p)=glue_node)or(type(p)=kern_node) then
> begin link(q):=link(p); link(p):=null; flush_node_list(p);
> end;
> end
11594,11597c14307,14310
< end;
< op_noad: begin delta←make_op(q);
< if subtype(q)=switched then goto check_dimensions;
< end;
---
> end;
> op_noad: begin delta:=make_op(q);
> if subtype(q)=limits then goto check_dimensions;
> end;
11607c14320,14321
< by procedures like |make_fraction|, |make_radical|, etc. To illustrate
---
> by procedures with names
> like |make_fraction|, |make_radical|, etc. To illustrate
11613,11615c14327,14330
< begin info(operand(q))←@|overbar(clean_box(operand(q),und_style(cur_style)),@|
< 3*default_rule_thickness,default_rule_thickness);
< math_type(operand(q))←sub_box;
---
> begin info(nucleus(q)):=@|
> overbar(clean_box(nucleus(q),cramped_style(cur_style)),@|
> 3*default_rule_thickness,default_rule_thickness);
> math_type(nucleus(q)):=sub_box;
11622,11628c14337,14343
< begin x←clean_box(operand(q),cur_style);
< p←new_kern(3*default_rule_thickness); link(x)←p;
< link(p)←fraction_rule(default_rule_thickness);
< y←vpack(x,natural);
< delta←height(y)+depth(y)+default_rule_thickness;
< height(y)←height(x); depth(y)←delta-height(y);
< info(operand(q))←y; math_type(operand(q))←sub_box;
---
> begin x:=clean_box(nucleus(q),cur_style);
> p:=new_kern(3*default_rule_thickness); link(x):=p;
> link(p):=fraction_rule(default_rule_thickness);
> y:=vpack(x,natural);
> delta:=height(y)+depth(y)+default_rule_thickness;
> height(y):=height(x); depth(y):=delta-height(y);
> info(nucleus(q)):=y; math_type(nucleus(q)):=sub_box;
11635,11640c14350,14355
< begin v←info(operand(q));
< if type(v)≠vlist_node then confusion("vcenter");
< @:confusion vcenter}{\quad vcenter@>
< delta←height(v)+depth(v);
< height(v)←axis_height(cur_size)+half(delta);
< depth(v)←height(v)-delta;
---
> begin v:=info(nucleus(q));
> if type(v)<>vlist_node then confusion("vcenter");
> @:this can't happen vcenter}{\quad vcenter@>
> delta:=height(v)+depth(v);
> height(v):=axis_height(cur_size)+half(delta);
> depth(v):=delta-height(v);
11645c14360
< between a square root sign and the rule above its operand by assuming that the
---
> between a square root sign and the rule above its nucleus by assuming that the
11649c14364
< of the operand plus a certain minimum clearance@@|clr|. The symbol will be
---
> of the nucleus plus a certain minimum clearance~|clr|. The symbol will be
11656,11668c14371,14382
< begin x←clean_box(operand(q),und_style(cur_style));
< if cur_style<text_style then
< clr←default_rule_thickness+(math_x_height(cur_size) div 4)
< else begin clr←default_rule_thickness; clr←clr + (clr div 4);
< end;
< y←var_delimiter(left_delimiter(q),cur_size,height(x)+depth(x)+clr+
< default_rule_thickness);
< if height(y)≤0 then height(y)←default_rule_thickness;
< delta←depth(y)-(height(x)+depth(x)+clr);
< if delta>0 then clr←clr+half(delta); {increase the actual clearance}
< shift_amount(y)←-(height(x)+clr);
< link(y)←overbar(x,clr,height(y));
< info(operand(q))←hpack(y,natural); math_type(operand(q))←sub_box;
---
> begin x:=clean_box(nucleus(q),cramped_style(cur_style));
> if cur_style<text_style then {display style}
> clr:=default_rule_thickness+(abs(math_x_height(cur_size)) div 4)
> else begin clr:=default_rule_thickness; clr:=clr + (abs(clr) div 4);
> end;
> y:=var_delimiter(left_delimiter(q),cur_size,height(x)+depth(x)+clr+
> default_rule_thickness);
> delta:=depth(y)-(height(x)+depth(x)+clr);
> if delta>0 then clr:=clr+half(delta); {increase the actual clearance}
> shift_amount(y):=-(height(x)+clr);
> link(y):=overbar(x,clr,height(y));
> info(nucleus(q)):=hpack(y,natural); math_type(nucleus(q)):=sub_box;
11677c14391
< label done;
---
> label done,done1;
11679c14393,14400
< @!delta:scaled; {amount to raise the accent}
---
> @!a:integer; {address of lig/kern instruction}
> @!c:quarterword; {accent character}
> @!f:internal_font_number; {its font}
> @!i:four_quarters; {its |char_info|}
> @!s:scaled; {amount to skew the accent to the right}
> @!h:scaled; {height of character being accented}
> @!delta:scaled; {space to remove between accent and accentee}
> @!w:scaled; {width of the accentee, not including sub/superscripts}
11682,11693c14403,14424
< begin x←clean_box(operand(q),und_style(cur_style));
< @<Switch to a larger accent if available and appropriate@>;
< delta←height(x)-x_height(cur_f);
< if delta<0 then delta←0;
< y←hpack(new_character(cur_f,cur_c),natural);
< shift_amount(y)←half(width(x)-width(y));
< width(y)←0; p←new_kern(delta-height(x)); link(p)←x; link(y)←p;
< y←vpack(y,natural); width(y)←width(x);
< info(operand(q))←y;
< math_type(operand(q))←sub_box;
< end;
< end;
---
> begin i:=cur_i; c:=cur_c; f:=cur_f;@/
> @<Compute the amount of skew@>;
> x:=clean_box(nucleus(q),cramped_style(cur_style)); w:=width(x); h:=height(x);
> @<Switch to a larger accent if available and appropriate@>;
> if h<x_height(f) then delta:=h@+else delta:=x_height(f);
> if (math_type(supscr(q))<>empty)or(math_type(subscr(q))<>empty) then
> if math_type(nucleus(q))=math_char then
> @<Swap the subscript and superscript into box |x|@>;
> y:=char_box(f,c);
> shift_amount(y):=s+half(w-width(y));
> width(y):=0; p:=new_kern(-delta); link(p):=x; link(y):=p;
> y:=vpack(y,natural); width(y):=width(x);
> if height(y)<h then @<Make the height of box |y| equal to |h|@>;
> info(nucleus(q)):=y;
> math_type(nucleus(q)):=sub_box;
> end;
> end;
>
> @ @<Make the height of box |y|...@>=
> begin p:=new_kern(h-height(y)); link(p):=list_ptr(y); list_ptr(y):=p;
> height(y):=h;
> end
11696,11701c14427,14433
< loop@+ begin if char_tag(cur_i)≠list_tag then goto done;
< y←rem_byte(cur_i);
< cur_i←char_info(cur_f)(y);
< if char_width(cur_f)(cur_i)>width(x) then goto done;
< cur_c←y;
< end;
---
> loop@+ begin if char_tag(i)<>list_tag then goto done;
> y:=rem_byte(i);
> i:=char_info(f)(y);
> if not char_exists(i) then goto done;
> if char_width(f)(i)>w then goto done;
> c:=y;
> end;
11703a14436,14470
> @ @<Compute the amount of skew@>=
> s:=0;
> if math_type(nucleus(q))=math_char then
> begin fetch(nucleus(q));
> if char_tag(cur_i)=lig_tag then
> begin a:=lig_kern_start(cur_f)(cur_i);
> cur_i:=font_info[a].qqqq;
> if skip_byte(cur_i)>stop_flag then
> begin a:=lig_kern_restart(cur_f)(cur_i);
> cur_i:=font_info[a].qqqq;
> end;
> loop@+ begin if qo(next_char(cur_i))=skew_char[cur_f] then
> begin if op_byte(cur_i)>=kern_flag then
> if skip_byte(cur_i)<=stop_flag then s:=char_kern(cur_f)(cur_i);
> goto done1;
> end;
> if skip_byte(cur_i)>=stop_flag then goto done1;
> a:=a+qo(skip_byte(cur_i))+1;
> cur_i:=font_info[a].qqqq;
> end;
> end;
> end;
> done1:
>
> @ @<Swap the subscript and superscript into box |x|@>=
> begin flush_node_list(x); x:=new_noad;
> mem[nucleus(x)]:=mem[nucleus(q)];
> mem[supscr(x)]:=mem[supscr(q)];
> mem[subscr(x)]:=mem[subscr(q)];@/
> mem[supscr(q)].hh:=empty_field;
> mem[subscr(q)].hh:=empty_field;@/
> math_type(nucleus(q)):=sub_mlist; info(nucleus(q)):=x;
> x:=clean_box(nucleus(q),cur_style); delta:=delta+height(x)-h; h:=height(x);
> end
>
11711,11714c14478,14482
< {dimensions for box calculations}
< begin @<Create equal-width boxes |x| and |z| for the numerator and denominator,
< and compute the default amounts |shift_up| and |shift_down| by which they
< are displaced from the baseline@>;
---
> {dimensions for box calculations}
> begin if thickness(q)=default_code then thickness(q):=default_rule_thickness;
> @<Create equal-width boxes |x| and |z| for the numerator and denominator,
> and compute the default amounts |shift_up| and |shift_down| by which they
> are displaced from the baseline@>;
11716c14484
< of no fraction line@>
---
> of no fraction line@>
11719c14487
< |shift_down|@>;
---
> |shift_down|@>;
11721c14489
< point to it@>;
---
> point to it@>;
11725,11735c14493,14503
< x←clean_box(numerator(q),num_style(cur_style));
< z←clean_box(denominator(q),denom_style(cur_style));
< if width(x)<width(z) then x←rebox(x,width(z))
< else z←rebox(z,width(x));
< if cur_style<text_style then
< begin shift_up←num1(cur_size); shift_down←denom1(cur_size);
< end
< else begin shift_down←denom2(cur_size);
< if thickness(q)≠0 then shift_up←num2(cur_size)
< else shift_up←num3(cur_size);
< end
---
> x:=clean_box(numerator(q),num_style(cur_style));
> z:=clean_box(denominator(q),denom_style(cur_style));
> if width(x)<width(z) then x:=rebox(x,width(z))
> else z:=rebox(z,width(x));
> if cur_style<text_style then {display style}
> begin shift_up:=num1(cur_size); shift_down:=denom1(cur_size);
> end
> else begin shift_down:=denom2(cur_size);
> if thickness(q)<>0 then shift_up:=num2(cur_size)
> else shift_up:=num3(cur_size);
> end
11742,11744c14510,14512
< begin if cur_style<text_style then clr←7*default_rule_thickness
< else clr←3*default_rule_thickness;
< delta←half(clr-((shift_up-depth(x))-(height(z)-shift_down)));
---
> begin if cur_style<text_style then clr:=7*default_rule_thickness
> else clr:=3*default_rule_thickness;
> delta:=half(clr-((shift_up-depth(x))-(height(z)-shift_down)));
11746,11748c14514,14516
< begin shift_up←shift_up+delta;
< shift_down←shift_down-delta;
< end;
---
> begin shift_up:=shift_up+delta;
> shift_down:=shift_down+delta;
> end;
11755,11762c14523,14529
< begin if thickness(q)=default_code then thickness(q)←default_rule_thickness;
< if cur_style<text_style then clr←3*thickness(q)
< else clr←thickness(q);
< delta←half(thickness(q));
< delta1←clr-((shift_up-depth(x))-(axis_height(cur_size)+delta));
< delta2←clr-((axis_height(cur_size)-delta)-(height(z)-shift_down));
< if delta1>0 then shift_up←shift_up+delta1;
< if delta2>0 then shift_down←shift_down+delta2;
---
> begin if cur_style<text_style then clr:=3*thickness(q)
> else clr:=thickness(q);
> delta:=half(thickness(q));
> delta1:=clr-((shift_up-depth(x))-(axis_height(cur_size)+delta));
> delta2:=clr-((axis_height(cur_size)-delta)-(height(z)-shift_down));
> if delta1>0 then shift_up:=shift_up+delta1;
> if delta2>0 then shift_down:=shift_down+delta2;
11766,11768c14533,14535
< v←new_null_box; type(v)←vlist_node;
< height(v)←shift_up+height(x); depth(v)←depth(z)+shift_down;
< width(v)←width(x); {this also equals |width(z)|}
---
> v:=new_null_box; type(v):=vlist_node;
> height(v):=shift_up+height(x); depth(v):=depth(z)+shift_down;
> width(v):=width(x); {this also equals |width(z)|}
11770,11779c14537,14546
< begin p←new_kern((shift_up-depth(x))-(height(z)-shift_down));
< link(p)←z;
< end
< else begin y←fraction_rule(thickness(q));@/
< p←new_kern((axis_height(cur_size)-delta)-@|(height(z)-shift_down));@/
< link(y)←p;@/
< p←new_kern((shift_up-depth(x))-(axis_height(cur_size)+delta));
< link(p)←y;
< end;
< link(x)←p; list_ptr(v)←x
---
> begin p:=new_kern((shift_up-depth(x))-(height(z)-shift_down));
> link(p):=z;
> end
> else begin y:=fraction_rule(thickness(q));@/
> p:=new_kern((axis_height(cur_size)-delta)-@|(height(z)-shift_down));@/
> link(y):=p; link(p):=z;@/
> p:=new_kern((shift_up-depth(x))-(axis_height(cur_size)+delta));
> link(p):=y;
> end;
> link(x):=p; list_ptr(v):=x
11782,11795c14549,14559
< if cur_style<text_style then delta←delim1(cur_size)
< else delta←delim2(cur_size);
< x←var_delimiter(left_delimiter(q), cur_size, delta); link(x)←v;@/
< z←var_delimiter(right_delimiter(q), cur_size, delta); link(v)←z;@/
< new_hlist(q)←hpack(x,natural)
<
< @ An |op_noad| is a bit confusing because its |operand| is really an
< ``operator.'' We shall occasionally call the operator an operand, since
< that is where the information appears in the data structure. If such an
< operand is a single character, it is to be centered vertically with
< respect to the axis, after first being enlarged (via a character list in
< the font) if we are in display style. The normal convention for placing
< displayed limits is to put them to the right only when the character has a
< nonzero italic correction.
---
> if cur_style<text_style then delta:=delim1(cur_size)
> else delta:=delim2(cur_size);
> x:=var_delimiter(left_delimiter(q), cur_size, delta); link(x):=v;@/
> z:=var_delimiter(right_delimiter(q), cur_size, delta); link(v):=z;@/
> new_hlist(q):=hpack(x,natural)
>
> @ If the nucleus of an |op_noad| is a single character, it is to be
> centered vertically with respect to the axis, after first being enlarged
> (via a character list in the font) if we are in display style. The normal
> convention for placing displayed limits is to put them above and below the
> operator in display style.
11797,11798c14561,14562
< The italic correction is removed from the character unless the limits are
< being placed at the right and there is no subscript. The |make_op|
---
> The italic correction is removed from the character if there is a subscript
> and the limits are not being displayed. The |make_op|
11802,11803c14566,14567
< After |make_op| has acted, |subtype(q)| will be |switched| if and only if
< the limits have been set above and below the operand. In that case,
---
> After |make_op| has acted, |subtype(q)| will be |limits| if and only if
> the limits have been set above and below the operator. In that case,
11809a14574
> @!c:quarterword;@+@!i:four_quarters; {registers for character examination}
11811,11832c14576,14596
< begin if math_type(operand(q))=math_char then
< begin fetch(operand(q));
< if (cur_style<text_style)∧(char_tag(cur_i)=list_tag) then {make it larger}
< begin cur_c←rem_byte(cur_i); character(operand(q))←cur_c;
< cur_i←char_info(cur_f)(cur_c);
< end;
< delta←char_italic(cur_f)(cur_i); x←clean_box(operand(q),cur_style);
< width(x)←width(x)-delta; {remove italic correction}
< shift_amount(x)←half(height(x)-depth(x)) - axis_height(cur_size);
< {center vertically}
< math_type(operand(q))←sub_box; info(operand(q))←x;
< end
< else delta←0;
< if cur_style<text_style then {display styles}
< if ((delta=0)∧(subtype(q)=normal))∨@|
< ((delta≠0)∧(subtype(q)=switched)) then subtype(q)←switched
< else subtype(q)←normal
< else subtype(q)←normal;
< if subtype(q)=switched then
< @<Construct a box with limits above and below it, skewed by |delta|@>
< else if (delta≠0)∧(math_type(subscr(q))=empty) then width(x)←width(x)+delta;
< make_op←delta;
---
> begin if (subtype(q)=normal)and(cur_style<text_style) then
> subtype(q):=limits;
> if math_type(nucleus(q))=math_char then
> begin fetch(nucleus(q));
> if (cur_style<text_style)and(char_tag(cur_i)=list_tag) then {make it larger}
> begin c:=rem_byte(cur_i); i:=char_info(cur_f)(c);
> if char_exists(i) then
> begin cur_c:=c; cur_i:=i; character(nucleus(q)):=c;
> end;
> end;
> delta:=char_italic(cur_f)(cur_i); x:=clean_box(nucleus(q),cur_style);
> if (math_type(subscr(q))<>empty)and(subtype(q)<>limits) then
> width(x):=width(x)-delta; {remove italic correction}
> shift_amount(x):=half(height(x)-depth(x)) - axis_height(cur_size);
> {center vertically}
> math_type(nucleus(q)):=sub_box; info(nucleus(q)):=x;
> end
> else delta:=0;
> if subtype(q)=limits then
> @<Construct a box with limits above and below it, skewed by |delta|@>;
> make_op:=delta;
11839,11847c14603,14611
< begin x←clean_box(supscr(q),sup_style(cur_style));
< y←clean_box(operand(q),cur_style);
< z←clean_box(subscr(q),sub_style(cur_style));
< v←new_null_box; type(v)←vlist_node; width(v)←width(y);
< if width(x)>width(v) then width(v)←width(x);
< if width(z)>width(v) then width(v)←width(z);
< x←rebox(x,width(v)); y←rebox(y,width(v)); z←rebox(z,width(v));@/
< shift_amount(x)←half(delta); shift_amount(z)←-shift_amount(x);
< height(v)←height(y); depth(v)←depth(y);
---
> begin x:=clean_box(supscr(q),sup_style(cur_style));
> y:=clean_box(nucleus(q),cur_style);
> z:=clean_box(subscr(q),sub_style(cur_style));
> v:=new_null_box; type(v):=vlist_node; width(v):=width(y);
> if width(x)>width(v) then width(v):=width(x);
> if width(z)>width(v) then width(v):=width(z);
> x:=rebox(x,width(v)); y:=rebox(y,width(v)); z:=rebox(z,width(v));@/
> shift_amount(x):=half(delta); shift_amount(z):=-shift_amount(x);
> height(v):=height(y); depth(v):=depth(y);
11849,11850c14613,14614
< account for their presence@>;
< new_hlist(q)←v;
---
> account for their presence@>;
> new_hlist(q):=v;
11860,11867c14624,14631
< begin free_node(x,box_node_size); list_ptr(v)←y;
< end
< else begin shift_up←big_op_spacing3-depth(x);
< if shift_up<big_op_spacing1 then shift_up←big_op_spacing1;
< p←new_kern(shift_up); link(p)←y; link(x)←p;@/
< p←new_kern(big_op_spacing5); link(p)←x; list_ptr(v)←p;
< height(v)←height(v)+big_op_spacing5+height(x)+depth(x)+shift_up;
< end;
---
> begin free_node(x,box_node_size); list_ptr(v):=y;
> end
> else begin shift_up:=big_op_spacing3-depth(x);
> if shift_up<big_op_spacing1 then shift_up:=big_op_spacing1;
> p:=new_kern(shift_up); link(p):=y; link(x):=p;@/
> p:=new_kern(big_op_spacing5); link(p):=x; list_ptr(v):=p;
> height(v):=height(v)+big_op_spacing5+height(x)+depth(x)+shift_up;
> end;
11869,11874c14633,14638
< else begin shift_down←big_op_spacing4-height(z);
< if shift_down<big_op_spacing2 then shift_down←big_op_spacing2;
< p←new_kern(shift_down); link(y)←p; link(p)←z;@/
< p←new_kern(big_op_spacing5); link(z)←p;
< depth(v)←depth(v)+big_op_spacing5+height(z)+depth(z)+shift_down;
< end
---
> else begin shift_down:=big_op_spacing4-height(z);
> if shift_down<big_op_spacing2 then shift_down:=big_op_spacing2;
> p:=new_kern(shift_down); link(y):=p; link(p):=z;@/
> p:=new_kern(big_op_spacing5); link(z):=p;
> depth(v):=depth(v)+big_op_spacing5+height(z)+depth(z)+shift_down;
> end
11879a14644,14649
> The |math_type| is converted to |math_text_char| here if we would not want to
> apply an italic correction to the current character unless it belongs
> to a math font (i.e., a font with |space=0|).
>
> No boundary characters enter into these ligatures.
>
11882c14652
< label restart;
---
> label restart,exit;
11884,11907c14654,14684
< @!p:pointer; {temporary register for list manipulation}
< begin restart:@;@/
< if (math_type(subscr(q))=empty)∧(math_type(supscr(q))=empty)∧@|
< (math_type(operand(q))=math_char) then
< begin p←link(q);
< if p≠null then if (type(p)≥ord_noad)∧(type(p)≤punct_noad) then
< if math_type(operand(p))=math_char then
< if fam(operand(p))=fam(operand(q)) then
< begin fetch(operand(q));
< if char_tag(cur_i)=lig_tag then
< begin a←lig_kern_start(cur_f)(cur_i);
< cur_c←character(operand(p));
< repeat cur_i←font_info[a].qqqq;@/
< @<If instruction |cur_i| is a kern with |cur_c|,
< attach the kern after |q|;
< or if it is a ligature with |cur_c|, combine
< noads |q| and |p| and |goto restart|@>;
< incr(a);
< until stop_bit(cur_i)≥stop_flag;
< end;
< end;
< end;
< end;
<
---
> @!p,@!r:pointer; {temporary registers for list manipulation}
> begin restart:@t@>@;@/
> if math_type(subscr(q))=empty then if math_type(supscr(q))=empty then
> if math_type(nucleus(q))=math_char then
> begin p:=link(q);
> if p<>null then if (type(p)>=ord_noad)and(type(p)<=punct_noad) then
> if math_type(nucleus(p))=math_char then
> if fam(nucleus(p))=fam(nucleus(q)) then
> begin math_type(nucleus(q)):=math_text_char;
> fetch(nucleus(q));
> if char_tag(cur_i)=lig_tag then
> begin a:=lig_kern_start(cur_f)(cur_i);
> cur_c:=character(nucleus(p));
> cur_i:=font_info[a].qqqq;
> if skip_byte(cur_i)>stop_flag then
> begin a:=lig_kern_restart(cur_f)(cur_i);
> cur_i:=font_info[a].qqqq;
> end;
> loop@+ begin @<If instruction |cur_i| is a kern with |cur_c|, attach
> the kern after~|q|; or if it is a ligature with |cur_c|, combine
> noads |q| and~|p| appropriately; then |return| if the cursor has
> moved past a noad, or |goto restart|@>;
> if skip_byte(cur_i)>=stop_flag then return;
> a:=a+qo(skip_byte(cur_i))+1;
> cur_i:=font_info[a].qqqq;
> end;
> end;
> end;
> end;
> exit:end;
>
11909c14686,14688
< is replaced by an |ord_noad|. Presumably a font designer will define such
---
> is replaced by an |ord_noad|, when the two noads collapse into one.
> But we could make a parenthesis (say) change shape when it follows
> certain letters. Presumably a font designer will define such
11911a14691,14692
> \chardef\?='174 % vertical line to indicate character retention
>
11913,11922c14694,14718
< if next_char(cur_i)=cur_c then
< if op_bit(cur_i)≥kern_flag then
< begin p←new_kern(char_kern(cur_f)(cur_i));
< link(p)←link(q); link(q)←p;
< end
< else begin link(q)←link(p); character(operand(q))←rem_byte(cur_i);@/
< mem[subscr(q)]←mem[subscr(p)];
< mem[supscr(q)]←mem[supscr(p)];
< free_node(p,noad_size); goto restart;
< end
---
> if next_char(cur_i)=cur_c then if skip_byte(cur_i)<=stop_flag then
> if op_byte(cur_i)>=kern_flag then
> begin p:=new_kern(char_kern(cur_f)(cur_i));
> link(p):=link(q); link(q):=p; return;
> end
> else begin check_interrupt; {allow a way out of infinite ligature loop}
> case op_byte(cur_i) of
> qi(1),qi(5): character(nucleus(q)):=rem_byte(cur_i); {\.{=:\?}, \.{=:\?>}}
> qi(2),qi(6): character(nucleus(p)):=rem_byte(cur_i); {\.{\?=:}, \.{\?=:>}}
> qi(3),qi(7),qi(11):begin r:=new_noad; {\.{\?=:\?}, \.{\?=:\?>}, \.{\?=:\?>>}}
> character(nucleus(r)):=rem_byte(cur_i);
> fam(nucleus(r)):=fam(nucleus(q));@/
> link(q):=r; link(r):=p;
> if op_byte(cur_i)<qi(11) then math_type(nucleus(r)):=math_char
> else math_type(nucleus(r)):=math_text_char; {prevent combination}
> end;
> othercases begin link(q):=link(p);
> character(nucleus(q)):=rem_byte(cur_i); {\.{=:}}
> mem[subscr(q)]:=mem[subscr(p)]; mem[supscr(q)]:=mem[supscr(p)];@/
> free_node(p,noad_size);
> end
> endcases;
> if op_byte(cur_i)>qi(3) then return;
> math_type(nucleus(q)):=math_char; goto restart;
> end
11926c14722
< |done_with_node|. Thus, |q|@@points to a noad whose operand may need to be
---
> |done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be
11930c14726
< If |operand(q)| is not a |math_char|, the variable |delta| is the amount
---
> If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount
11936,11947c14732,14745
< @<Convert \(o)|operand(q)| to an hlist and attach the sub/superscripts@>=
< case math_type(operand(q)) of
< math_char: @<Create a character node |p| for |operand(q)|, possibly followed
< by a kern node for the italic correction, and set |delta| to the
< italic correction if a subscript is present@>;
< empty: p←null;
< sub_box: p←info(operand(q));
< sub_mlist: begin cur_mlist←info(operand(q)); save_style←cur_style;
< mlist_penalties←false; mlist_to_hlist; {recursive call}
< cur_style←save_style; @<Set up the values...@>;
< p←hpack(link(temp_head),natural);
< end;
---
> @<Convert \(n)|nucleus(q)| to an hlist and attach the sub/superscripts@>=
> case math_type(nucleus(q)) of
> math_char, math_text_char:
> @<Create a character node |p| for |nucleus(q)|, possibly followed
> by a kern node for the italic correction, and set |delta| to the
> italic correction if a subscript is present@>;
> empty: p:=null;
> sub_box: p:=info(nucleus(q));
> sub_mlist: begin cur_mlist:=info(nucleus(q)); save_style:=cur_style;
> mlist_penalties:=false; mlist_to_hlist; {recursive call}
> @^recursion@>
> cur_style:=save_style; @<Set up the values...@>;
> p:=hpack(link(temp_head),natural);
> end;
11949c14747
< @:confusion mlist2}{\quad mlist2@>
---
> @:this can't happen mlist2}{\quad mlist2@>
11951,11953c14749,14751
< new_hlist(q)←p;
< if (math_type(subscr(q))=empty)∧(math_type(supscr(q))=empty) then
< goto check_dimensions;
---
> new_hlist(q):=p;
> if (math_type(subscr(q))=empty)and(math_type(supscr(q))=empty) then
> goto check_dimensions;
11956,11957c14754,14755
< @ @<Create a character node |p| for |operand(q)|...@>=
< begin fetch(operand(q));
---
> @ @<Create a character node |p| for |nucleus(q)|...@>=
> begin fetch(nucleus(q));
11959,11964c14757,14764
< begin delta←char_italic(cur_f)(cur_i); p←new_character(cur_f,cur_c);
< if (math_type(subscr(q))=empty)∧(delta≠0) then
< begin link(p)←new_kern(delta); delta←0;
< end;
< end
< else p←null;
---
> begin delta:=char_italic(cur_f)(cur_i); p:=new_character(cur_f,qo(cur_c));
> if (math_type(nucleus(q))=math_text_char)and(space(cur_f)<>0) then
> delta:=0; {no italic correction in mid-word of text font}
> if (math_type(subscr(q))=empty)and(delta<>0) then
> begin link(p):=new_kern(delta); delta:=0;
> end;
> end
> else p:=null;
11969c14769
< given that subscript and superscript aren't both empty. The superscript
---
> given that the subscript and superscript aren't both empty. The superscript
11973c14773
< baseline of subscripts and superscripts based on the given operand.
---
> baseline of subscripts and superscripts based on the given nucleus.
11980c14780
< begin p←new_hlist(q);
---
> begin p:=new_hlist(q);
11982,11989c14782,14789
< begin shift_up←0; shift_down←0;
< end
< else begin z←hpack(p,natural);
< if cur_style<script_style then t←script_size@+else t←script_script_size;
< shift_up←height(z)-sup_drop(t);
< shift_down←depth(z)+sub_drop(t);
< free_node(z,box_node_size);
< end;
---
> begin shift_up:=0; shift_down:=0;
> end
> else begin z:=hpack(p,natural);
> if cur_style<script_style then t:=script_size@+else t:=script_script_size;
> shift_up:=height(z)-sup_drop(t);
> shift_down:=depth(z)+sub_drop(t);
> free_node(z,box_node_size);
> end;
11991,12001c14791,14801
< @<Construct a subscript box |x| when there is no superscript@>
< else begin @<Construct a superscript box |x|@>;
< if math_type(subscr(q))=empty then shift_amount(x)←-shift_up
< else @<Construct a sub/superscript combination box |x|, with the
< superscript offset by |delta|@>;
< end;
< if new_hlist(q)=null then new_hlist(q)←x
< else begin p←new_hlist(q);
< while link(p)≠null do p←link(p);
< link(p)←x;
< end;
---
> @<Construct a subscript box |x| when there is no superscript@>
> else begin @<Construct a superscript box |x|@>;
> if math_type(subscr(q))=empty then shift_amount(x):=-shift_up
> else @<Construct a sub/superscript combination box |x|, with the
> superscript offset by |delta|@>;
> end;
> if new_hlist(q)=null then new_hlist(q):=x
> else begin p:=new_hlist(q);
> while link(p)<>null do p:=link(p);
> link(p):=x;
> end;
12008,12013c14808,14813
< begin x←clean_box(subscr(q),sub_style(cur_style));
< width(x)←width(x)+script_space;
< if shift_down<sub1(cur_size) then shift_down←sub1(cur_size);
< clr←height(x)-((math_x_height(cur_size)*4) div 5);
< if shift_down<clr then shift_down←clr;
< shift_amount(x)←shift_down;
---
> begin x:=clean_box(subscr(q),sub_style(cur_style));
> width(x):=width(x)+script_space;
> if shift_down<sub1(cur_size) then shift_down:=sub1(cur_size);
> clr:=height(x)-(abs(math_x_height(cur_size)*4) div 5);
> if shift_down<clr then shift_down:=clr;
> shift_amount(x):=shift_down;
12020,12027c14820,14827
< begin x←clean_box(supscr(q),sup_style(cur_style));
< width(x)←width(x)+script_space;
< if odd(cur_style) then clr←sup3(cur_size)
< else if cur_style<text_style then clr←sup1(cur_size)
< else clr←sup2(cur_size);
< if shift_up<clr then shift_up←clr;
< clr←depth(x)+(math_x_height(cur_size) div 4);
< if shift_up<clr then shift_up←clr;
---
> begin x:=clean_box(supscr(q),sup_style(cur_style));
> width(x):=width(x)+script_space;
> if odd(cur_style) then clr:=sup3(cur_size)
> else if cur_style<text_style then clr:=sup1(cur_size)
> else clr:=sup2(cur_size);
> if shift_up<clr then shift_up:=clr;
> clr:=depth(x)+(abs(math_x_height(cur_size)) div 4);
> if shift_up<clr then shift_up:=clr;
12031c14831
< at least four times |default_rule_thickness| away from the superscript.
---
> separated from the superscript by at least four times |default_rule_thickness|.
12037,12041c14837,14841
< begin y←clean_box(subscr(q),sub_style(cur_style));
< width(y)←width(y)+script_space;
< if shift_down<sub2(cur_size) then shift_down←sub2(cur_size);
< clr←4*default_rule_thickness-
< ((shift_up-depth(x))-(height(y)-shift_down));
---
> begin y:=clean_box(subscr(q),sub_style(cur_style));
> width(y):=width(y)+script_space;
> if shift_down<sub2(cur_size) then shift_down:=sub2(cur_size);
> clr:=4*default_rule_thickness-
> ((shift_up-depth(x))-(height(y)-shift_down));
12043,12052c14843,14852
< begin shift_down←shift_down+clr;
< clr←(4*math_x_height(cur_size) div 5)-(shift_up-depth(x));
< if clr>0 then
< begin shift_up←shift_up+clr;
< shift_down←shift_down-clr;
< end;
< end;
< shift_amount(x)←delta; {superscript is |delta| to the right of the subscript}
< p←new_kern((shift_up-depth(x))-(height(y)-shift_down)); link(x)←p; link(p)←y;
< x←vpack(x,natural); shift_amount(x)←shift_down;
---
> begin shift_down:=shift_down+clr;
> clr:=(abs(math_x_height(cur_size)*4) div 5)-(shift_up-depth(x));
> if clr>0 then
> begin shift_up:=shift_up+clr;
> shift_down:=shift_down-clr;
> end;
> end;
> shift_amount(x):=delta; {superscript is |delta| to the right of the subscript}
> p:=new_kern((shift_up-depth(x))-(height(y)-shift_down)); link(x):=p; link(p):=y;
> x:=vpack(x,natural); shift_amount(x):=shift_down;
12054c14854
<
---
>
12062c14862
< p←temp_head; link(p)←null; q←mlist; r_type←0; cur_style←style;
---
> p:=temp_head; link(p):=null; q:=mlist; r_type:=0; cur_style:=style;
12064,12074c14864,14874
< while q≠null do
< begin @<If node |q| is a style node, change the style and |goto delete_q|;
< otherwise if it is not a noad, put it into the hlist,
< advance |q|, and |goto done|; otherwise set |s| to the size
< of noad |q|, set |t| to the associated type (|ord_noad..
< inner_noad|), and set |pen| to the associated penalty@>;
< @<Append inter-element spacing based on |r_type| and |t|@>;
< @<Append any |new_hlist| entries for |q|, and any appropriate penalties@>;
< r_type←t;
< delete_q: r←q; q←link(q); free_node(r,s);
< done: end
---
> while q<>null do
> begin @<If node |q| is a style node, change the style and |goto delete_q|;
> otherwise if it is not a noad, put it into the hlist,
> advance |q|, and |goto done|; otherwise set |s| to the size
> of noad |q|, set |t| to the associated type (|ord_noad..
> inner_noad|), and set |pen| to the associated penalty@>;
> @<Append inter-element spacing based on |r_type| and |t|@>;
> @<Append any |new_hlist| entries for |q|, and any appropriate penalties@>;
> r_type:=t;
> delete_q: r:=q; q:=link(q); free_node(r,s);
> done: end
12076,12077c14876,14877
< @ Before we do the big |case| switch in the second pass, we set the default
< values so that most of the branches are short.
---
> @ Just before doing the big |case| switch in the second pass, the program
> sets up default values so that most of the branches are short.
12080c14880
< t←inner_noad; s←noad_size; pen←inf_penalty;
---
> t:=ord_noad; s:=noad_size; pen:=inf_penalty;
12082,12091c14882,14892
< ord_noad,op_noad,open_noad,close_noad,punct_noad: t←type(q);
< bin_noad: begin t←bin_noad; pen←bin_op_penalty;
< end;
< rel_noad: begin t←rel_noad; pen←rel_penalty;
< end;
< inner_noad,vcenter_noad,over_noad,under_noad: do_nothing;
< radical_noad: s←radical_noad_size;
< accent_noad: s←accent_noad_size;
< fraction_noad: s←fraction_noad_size;
< left_noad,right_noad: t←make_left_right(q,style,max_d,max_h);
---
> op_noad,open_noad,close_noad,punct_noad,inner_noad: t:=type(q);
> bin_noad: begin t:=bin_noad; pen:=bin_op_penalty;
> end;
> rel_noad: begin t:=rel_noad; pen:=rel_penalty;
> end;
> ord_noad,vcenter_noad,over_noad,under_noad: do_nothing;
> radical_noad: s:=radical_noad_size;
> accent_noad: s:=accent_noad_size;
> fraction_noad: begin t:=inner_noad; s:=fraction_noad_size;
> end;
> left_noad,right_noad: t:=make_left_right(q,style,max_d,max_h);
12093,12096c14894,14897
< whatsit_node,penalty_node,rule_node,disc_node,
< glue_node,kern_node: begin@t@>@;@/
< link(p)←q; q←link(q); link(p)←null; goto done;
< end;
---
> whatsit_node,penalty_node,rule_node,disc_node,adjust_node,ins_node,mark_node,
> glue_node,kern_node:@t@>@;@/
> begin link(p):=q; p:=q; q:=link(q); link(p):=null; goto done;
> end;
12098c14899
< @:confusion mlist3}{\quad mlist3@>
---
> @:this can't happen mlist3}{\quad mlist3@>
12101,12104c14902,14905
< @ The |make_left_right| function constructs a delimiter of the required size
< and returns the value |open_noad| or |close_noad|. The |right_noad| and
< |left_noad| will both be based on the original |style|, so they will
< have consistent sizes.
---
> @ The |make_left_right| function constructs a left or right delimiter of
> the required size and returns the value |open_noad| or |close_noad|. The
> |right_noad| and |left_noad| will both be based on the original |style|,
> so they will have consistent sizes.
12110c14911
< @!max_d,@!max_h:scaled):small_number;
---
> @!max_d,@!max_h:scaled):small_number;
12112,12121c14913,14922
< begin if style<script_style then cur_size←text_size
< else cur_size←16*((style-text_style) div 2);
< delta2←max_d+axis_height(cur_size);
< delta1←max_h+max_d-delta2;
< if delta2>delta1 then delta1←delta2; {|delta1| is max distance from axis}
< delta←(delimiter_factor*delta1) div 500;
< delta2←delta1+delta1-delimiter_limit;
< if delta<delta2 then delta←delta2;
< new_hlist(q)←var_delimiter(delimiter(q),cur_size,delta);
< make_left_right←type(q)-(left_noad-open_noad); {|open_noad| or |close_noad|}
---
> begin if style<script_style then cur_size:=text_size
> else cur_size:=16*((style-text_style) div 2);
> delta2:=max_d+axis_height(cur_size);
> delta1:=max_h+max_d-delta2;
> if delta2>delta1 then delta1:=delta2; {|delta1| is max distance from axis}
> delta:=(delta1 div 500)*delimiter_factor;
> delta2:=delta1+delta1-delimiter_shortfall;
> if delta<delta2 then delta:=delta2;
> new_hlist(q):=var_delimiter(delimiter(q),cur_size,delta);
> make_left_right:=type(q)-(left_noad-open_noad); {|open_noad| or |close_noad|}
12125c14926
< begin cur_style←subtype(q); s←small_node_size;
---
> begin cur_style:=subtype(q); s:=style_node_size;
12135,12138c14936,14941
< \.1 means a conditional thin space (\.{\\nonscript\\mskip\\the\\thinmskip});\cr
< \.2 means a thin space (\.{\\mskip\\the\\thinmskip});\cr
< \.3 means a conditional medium space (\.{\\nonscript\\mskip\\the\\medmskip});\cr
< \.4 means a thick space (\.{\\mskip\\the\\thickmskip});\cr
---
> \.1 means a conditional thin space (\.{\\nonscript\\mskip\\thinmuskip});\cr
> \.2 means a thin space (\.{\\mskip\\thinmuskip});\cr
> \.3 means a conditional medium space
> (\.{\\nonscript\\mskip\\medmuskip});\cr
> \.4 means a conditional thick space
> (\.{\\nonscript\\mskip\\thickmuskip});\cr
12140,12141c14943,14945
< This is all pretty cryptic, but the \TeX\ manual explains what is supposed to
< happen, and the string makes it happen.
---
> This is all pretty cryptic, but {\sl The \TeX book\/} explains what is
> supposed to happen, and the string makes it happen.
> @:TeXbook}{\sl The \TeX book@>
12153,12154c14957,14958
< "0234000022*4000033**3**344*0400400*000000234000011*4111102340000"
< @t\hskip-35pt@>
---
> "0234000122*4000133**3**344*0400400*000000234000111*1111112341011"
> @t$ \hskip-35pt$@>
12160c14964
< magic_offset←str_start[math_spacing]-9*ord_noad
---
> magic_offset:=str_start[math_spacing]-9*ord_noad
12164,12178c14968,14982
< begin case str_pool[r_type*8+t+magic_offset] of
< "0": x←0;
< "1": if cur_style<script_style then x←thin_mskip_code@+else x←0;
< "2": x←thin_mskip_code;
< "3": if cur_style<script_style then x←med_mskip_code@+else x←0;
< "4": x←thick_mskip_code;
< othercases confusion("mlist4")
< @:confusion mlist4}{\quad mlist4@>
< endcases;
< if x≠0 then
< begin y←math_glue(glue_par(x),cur_mu);
< z←new_glue(y); glue_ref_count(y)←null; link(p)←z; p←z;@/
< subtype(z)←x+1; {store a symbolic subtype}
< end;
< end
---
> begin case so(str_pool[r_type*8+t+magic_offset]) of
> "0": x:=0;
> "1": if cur_style<script_style then x:=thin_mu_skip_code@+else x:=0;
> "2": x:=thin_mu_skip_code;
> "3": if cur_style<script_style then x:=med_mu_skip_code@+else x:=0;
> "4": if cur_style<script_style then x:=thick_mu_skip_code@+else x:=0;
> othercases confusion("mlist4")
> @:this can't happen mlist4}{\quad mlist4@>
> endcases;
> if x<>0 then
> begin y:=math_glue(glue_par(x),cur_mu);
> z:=new_glue(y); glue_ref_count(y):=null; link(p):=z; p:=z;@/
> subtype(z):=x+1; {store a symbolic subtype}
> end;
> end
12185,12196c14989,15001
< if new_hlist(q)≠null then
< begin link(p)←new_hlist(q);
< repeat p←link(p);
< until link(p)=null;
< end;
< if (pen<inf_penalty)∧ penalties ∧(link(q)≠null) then
< begin r_type←type(link(q));
< if (r_type≠penalty_node)∧(r_type≠rel_noad) then
< begin z←new_penalty(pen); link(p)←z; p←z;
< end;
< end
< @* \[36] Alignment.
---
> if new_hlist(q)<>null then
> begin link(p):=new_hlist(q);
> repeat p:=link(p);
> until link(p)=null;
> end;
> if penalties then if link(q)<>null then if pen<inf_penalty then
> begin r_type:=type(link(q));
> if r_type<>penalty_node then if r_type<>rel_noad then
> begin z:=new_penalty(pen); link(p):=z; p:=z;
> end;
> end
>
> @* \[37] Alignment.
12198c15003,15005
< they cut across so many of the control structures of \TeX. There\-fore the
---
> they cut across so many of the control structures of \TeX.
>
> Therefore the
12220,12221c15027,15028
< places the 300pt dimension onto |save_stack|, and the code |align_group|
< is placed above it. This will make it possible to complete the alignment
---
> places the 300pt dimension onto the |save_stack|, and an |align_group|
> code is placed above it. This will make it possible to complete the alignment
12247c15054
< token list `\.{u1}', i.e., the template preceding the `\.\#' for column@@1.
---
> token list `\.{u1}', i.e., the template preceding the `\.\#' for column~1.
12258c15065
< alignrecord for column@@1; in general, the alignrecords will record the
---
> alignrecord for column~1; in general, the alignrecords will record the
12261c15068
< (3) Since `\.{\\omit}' folows the `\.\&', the templates for column@@2
---
> (3) Since `\.{\\omit}' follows the `\.\&', the templates for column~2
12265c15072
< is remembered in the |width| field of the alignrecord for column@@2.
---
> is remembered in the |width| field of the alignrecord for column~2.
12268c15075
< mecha\-nism that worked for column@@1; this unset box contains `\.{u3\\vrule
---
> mechanism that worked for column~1; this unset box contains `\.{u3\\vrule
12281c15088
< {}\\unsetbox for 1 column: u3\vrule v3\cr
---
> {}\\unsetbox for 1 column: u3\\vrule v3\cr
12299c15106
< The natural width of the unset box that spans columns 1@@and@@2 is stored
---
> The natural width of the unset box that spans columns 1~and~2 is stored
12301c15108
< alignrecord for column@@1 now points to the new span node, and the |info|
---
> alignrecord for column~1 now points to the new span node, and the |info|
12330,12331c15137,15138
< @d u_part(#)==mem[#+height_offset].int {pointer to \<u↓j> token list}
< @d v_part(#)==mem[#+depth_offset].int {pointer to \<v↓j> token list}
---
> @d u_part(#)==mem[#+height_offset].int {pointer to \<u_j> token list}
> @d v_part(#)==mem[#+depth_offset].int {pointer to \<v_j> token list}
12339,12341c15146,15152
< spanned columns; and the |align_state| variable, which indicates the nesting
< of braces so that \.{\\cr} and \.{\\span} and tab marks are properly
< intercepted.
---
> spanned columns; a |cur_loop| pointer, indicating the tabskip glue before
> an alignrecord that should be copied next if the current list is extended;
> and the |align_state| variable, which indicates the nesting of braces so
> that \.{\\cr} and \.{\\span} and tab marks are properly intercepted.
> There also are pointers |cur_head| and |cur_tail| to the head and tail
> of a list of adjustments being moved out from horizontal mode to
> vertical~mode.
12343,12344c15154,15155
< The current values of these four quantities appear in global variables;
< when they have to be pushed down, they are stored in 3-word nodes, and
---
> The current values of these seven quantities appear in global variables;
> when they have to be pushed down, they are stored in 5-word nodes, and
12348c15159
< @d align_stack_node_size=3 {number of |mem| words to save alignment states}
---
> @d align_stack_node_size=5 {number of |mem| words to save alignment states}
12352a15164
> @!cur_loop:pointer; {place to copy when extending a periodic preamble}
12353a15166
> @!cur_head,@!cur_tail:pointer; {adjustment list pointers}
12358c15171,15172
< align_ptr←null; cur_align←null; cur_span←null;
---
> align_ptr:=null; cur_align:=null; cur_span:=null; cur_loop:=null;
> cur_head:=null; cur_tail:=null;
12365,12368c15179,15185
< begin p←get_node(align_stack_node_size);
< link(p)←align_ptr; info(p)←cur_align;
< llink(p)←preamble; rlink(p)←cur_span;
< mem[p+2].int←align_state; align_ptr←p;
---
> begin p:=get_node(align_stack_node_size);
> link(p):=align_ptr; info(p):=cur_align;
> llink(p):=preamble; rlink(p):=cur_span;
> mem[p+2].int:=cur_loop; mem[p+3].int:=align_state;
> info(p+4):=cur_head; link(p+4):=cur_tail;
> align_ptr:=p;
> cur_head:=get_avail;
12373,12375c15190,15195
< begin p←align_ptr; align_state←mem[p+2].int;
< cur_span←rlink(p); preamble←llink(p);
< cur_align←info(p); align_ptr←link(p);
---
> begin free_avail(cur_head);
> p:=align_ptr;
> cur_tail:=link(p+4); cur_head:=info(p+4);
> align_state:=mem[p+3].int; cur_loop:=mem[p+2].int;
> cur_span:=rlink(p); preamble:=llink(p);
> cur_align:=info(p); align_ptr:=link(p);
12399c15219
< procedure@?off_save; forward;@t\2@>@/
---
> procedure@?normal_paragraph; forward;@t\2@>@/
12404,12405c15224,15225
< begin save_cs_ptr←cs_ptr; {\.{\\halign} or \.{\\valign}, usually}
< push_alignment; align_state←-1000000; {enter a new alignment level}
---
> begin save_cs_ptr:=cur_cs; {\.{\\halign} or \.{\\valign}, usually}
> push_alignment; align_state:=-1000000; {enter a new alignment level}
12409c15229
< scan_spec; new_save_level(align_group);
---
> scan_spec(align_group,false);@/
12410a15231,15232
> new_save_level(align_group);
> if every_cr<>null then begin_token_list(every_cr,every_cr_text);
12421,12425c15243,15245
< begin mode←-vmode; prev_depth←nest[nest_ptr-2].aux_field;
< end
< else begin if mode>0 then mode←-mode;
< if mode=-hmode then space_factor←1000;
< end
---
> begin mode:=-vmode; prev_depth:=nest[nest_ptr-2].aux_field.sc;
> end
> else if mode>0 then negate(mode)
12428,12430c15248
< no other pieces of mlists present. Incomplete mlists need to be
< deleted; and we must convert them to hlists first, since the
< |flush_node_list| procedure doesn't know about noads.
---
> no other pieces of mlists present.
12433,12445c15251,15258
< if (mode=mmode)∧((tail≠head)∨(incompleat_noad≠null)) then
< begin print_nl("! Improper \halign");
< @.Improper \\halign@>
< help3("Displays can use special alignments (like \eqalignno)")@/
< ("only if nothing but the alignment itself is between $$'s.")@/
< ("So I've deleted the formulas that preceded this alignment.");
< error;@/
< cur_style←display_style; mlist_penalties←false;
< cur_mlist←link(head); mlist_to_hlist; flush_node_list(link(temp_head));@/
< cur_style←display_style; cur_mlist←incompleat_noad;
< mlist_to_hlist; {|mlist_penalties=true|}
< flush_node_list(link(temp_head));
< end
---
> if (mode=mmode)and((tail<>head)or(incompleat_noad<>null)) then
> begin print_err("Improper "); print_esc("halign"); print(" inside $$'s");
> @.Improper \\halign...@>
> help3("Displays can use special alignments (like \eqalignno)")@/
> ("only if nothing but the alignment itself is between $$'s.")@/
> ("So I've deleted the formulas that preceded this alignment.");
> error; flush_math;
> end
12448,12457c15261,15270
< preamble←null; cur_align←align_head; scanner_status←aligning;
< warning_index←save_cs_ptr;
< {at this point, |cur_cmd=left_brace| and |align_state=-999999|}
< loop@+ begin @<Append the current tabskip glue to the preamble list@>;
< if cur_cmd=car_ret then goto done; {\.{\\cr} ends the preamble}
< @<Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret|,
< looking for changes in the tabskip glue; append an
< alignrecord to the preamble list@>;
< end;
< done: scanner_status←normal
---
> preamble:=null; cur_align:=align_head; cur_loop:=null; scanner_status:=aligning;
> warning_index:=save_cs_ptr; align_state:=-1000000;
> {at this point, |cur_cmd=left_brace|}
> loop@+ begin @<Append the current tabskip glue to the preamble list@>;
> if cur_cmd=car_ret then goto done; {\.{\\cr} ends the preamble}
> @<Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret|,
> looking for changes in the tabskip glue; append an
> alignrecord to the preamble list@>;
> end;
> done: scanner_status:=normal
12460,12461c15273,15274
< link(cur_align)←new_param_glue(tab_skip_code);
< cur_align←link(cur_align)
---
> link(cur_align):=new_param_glue(tab_skip_code);
> cur_align:=link(cur_align)
12464,12469c15277,15282
< @<Scan the template \<u↓j>, putting the resulting token list in |hold_head|@>;
< link(cur_align)←new_null_box; cur_align←link(cur_align); {a new alignrecord}
< info(cur_align)←end_span; width(cur_align)←null_flag;
< u_part(cur_align)←link(hold_head);
< @<Scan the template \<v↓j>, putting the resulting token list in |hold_head|@>;
< v_part(cur_align)←link(hold_head)
---
> @<Scan the template \<u_j>, putting the resulting token list in |hold_head|@>;
> link(cur_align):=new_null_box; cur_align:=link(cur_align); {a new alignrecord}
> info(cur_align):=end_span; width(cur_align):=null_flag;
> u_part(cur_align):=link(hold_head);
> @<Scan the template \<v_j>, putting the resulting token list in |hold_head|@>;
> v_part(cur_align):=link(hold_head)
12472c15285
< and with |span_code| as the operand modifier. This makes \TeX\ interpret it
---
> and with |span_code| as the command modifier. This makes \TeX\ interpret it
12475c15288,15289
< It also turns out to be useful to give a special |cr_code| to `\.{\\cr}'.
---
> It also turns out to be useful to give a special |cr_code| to `\.{\\cr}',
> and an even larger |cr_cr_code| to `\.{\\crcr}'.
12477,12478c15291,15300
< @d span_code=128 {distinct from any character}
< @d cr_code=129 {distinct from |span_code| and from any character}
---
> The end of a template is represented by two ``frozen'' control sequences
> called \.{\\endtemplate}. The first has the command code |end_template|, which
> is |>outer_call|, so it will not easily disappear in the presence of errors.
> The |get_x_token| routine converts the first into the second, which has |endv|
> as its command code.
>
> @d span_code=256 {distinct from any character}
> @d cr_code=257 {distinct from |span_code| and from any character}
> @d cr_cr_code=cr_code+1 {this distinguishes \.{\\crcr} from \.{\\cr}}
> @d end_template_token==cs_token_flag+frozen_end_template
12481c15303
< primitive("span",tab_mark,span_code);
---
> primitive("span",tab_mark,span_code);@/
12484a15307,15314
> text(frozen_cr):="cr"; eqtb[frozen_cr]:=eqtb[cur_val];@/
> primitive("crcr",car_ret,cr_cr_code);
> @!@:cr_cr_}{\.{\\crcr} primitive@>
> text(frozen_end_template):="endtemplate"; text(frozen_endv):="endtemplate";
> eq_type(frozen_endv):=endv; equiv(frozen_endv):=null_list;
> eq_level(frozen_endv):=level_one;@/
> eqtb[frozen_end_template]:=eqtb[frozen_endv];
> eq_type(frozen_end_template):=end_template;
12486c15316,15322
< @ The preamble is copied directly, except that \.{\\tabskip} causes a change
---
> @ @<Cases of |print_cmd_chr|...@>=
> tab_mark: if chr_code=span_code then print_esc("span")
> else chr_cmd("alignment tab character ");
> car_ret: if chr_code=cr_code then print_esc("cr")
> else print_esc("crcr");
>
> @ The preamble is copied directly, except that \.{\\tabskip} causes a change
12488c15324
< follow it.
---
> follow it. An appearance of \.{\\span} also causes such an expansion.
12496,12509c15332,15348
< label reswitch;
< begin reswitch: get_token;
< if (cur_cmd=assign_glue)∧(cur_chr=tab_skip_code) then
< begin scan_optional_equals; scan_glue(false);
< eq_define(glue_base+tab_skip_code,glue_ref,cur_val);
< goto reswitch;
< end;
< if (cur_chr=span_code)∧(cur_cmd=tab_mark) then
< begin print_nl("! Illegal preamble");
< @.Illegal preamble@>
< help2("You mustn't say \span in an alignment until after the \cr")@/
< ("that ends the preamble. But proceed; I'll ignore this \span.");@/
< error; goto reswitch;
< end;
---
> label restart;
> begin restart: get_token;
> while (cur_chr=span_code)and(cur_cmd=tab_mark) do
> begin get_token; {this token will be expanded once}
> if cur_cmd>max_command then
> begin expand; get_token;
> end;
> end;
> if cur_cmd=endv then
> fatal_error("(interwoven alignment preambles are not allowed)");
> @.interwoven alignment preambles...@>
> if (cur_cmd=assign_glue)and(cur_chr=glue_base+tab_skip_code) then
> begin scan_optional_equals; scan_glue(glue_val);
> if global_defs>0 then geq_define(glue_base+tab_skip_code,glue_ref,cur_val)
> else eq_define(glue_base+tab_skip_code,glue_ref,cur_val);
> goto restart;
> end;
12514,12529c15353,15370
< @<Scan the template \<u↓j>...@>=
< p←hold_head; link(p)←null;
< loop@+ begin get_preamble_token;
< if cur_cmd=mac_param then goto done1;
< if (cur_cmd≤car_ret)∧(cur_cmd≥tab_mark) then
< begin print_nl("! Missing # inserted in alignment preamble");
< @.Missing {\#} inserted...@>
< help3("There should be exactly one # between &'s, when an")@/
< ("\halign or \valign is being set up. In this case you had")@/
< ("none, so I've put one in; maybe that will work.");
< back_error; goto done1;
< end;
< if (cur_cmd≠spacer)∨(p≠hold_head) then
< begin link(p)←get_avail; p←link(p); info(p)←cur_tok;
< end;
< end;
---
> @<Scan the template \<u_j>...@>=
> p:=hold_head; link(p):=null;
> loop@+ begin get_preamble_token;
> if cur_cmd=mac_param then goto done1;
> if (cur_cmd<=car_ret)and(cur_cmd>=tab_mark)and(align_state=-1000000) then
> if (p=hold_head)and(cur_loop=null)and(cur_cmd=tab_mark)
> then cur_loop:=cur_align
> else begin print_err("Missing # inserted in alignment preamble");
> @.Missing \# inserted...@>
> help3("There should be exactly one # between &'s, when an")@/
> ("\halign or \valign is being set up. In this case you had")@/
> ("none, so I've put one in; maybe that will work.");
> back_error; goto done1;
> end
> else if (cur_cmd<>spacer)or(p<>hold_head) then
> begin link(p):=get_avail; p:=link(p); info(p):=cur_tok;
> end;
> end;
12532,12548c15373,15389
< @ @<Scan the template \<v↓j>...@>=
< p←hold_head; link(p)←null;
< loop@+ begin continue: get_preamble_token;
< if (cur_cmd≤car_ret)∧(cur_cmd≥tab_mark)∧(align_state=-1000000) then
< goto done2;
< if cur_cmd=mac_param then
< begin print_nl("! Only one # is allowed per tab");
< @.Only one {\#} is allowed...@>
< help3("There should be exactly one # between &'s, when an")@/
< ("\halign or \valign is being set up. In this case you had")@/
< ("more than one, so I'm ignoring all but the first.");
< goto continue;
< end;
< link(p)←get_avail; p←link(p); info(p)←cur_tok;
< end;
< done2: link(p)←get_avail; p←link(p);
< info(p)←endv_token {put |endv| at the end}
---
> @ @<Scan the template \<v_j>...@>=
> p:=hold_head; link(p):=null;
> loop@+ begin continue: get_preamble_token;
> if (cur_cmd<=car_ret)and(cur_cmd>=tab_mark)and(align_state=-1000000) then
> goto done2;
> if cur_cmd=mac_param then
> begin print_err("Only one # is allowed per tab");
> @.Only one \# is allowed...@>
> help3("There should be exactly one # between &'s, when an")@/
> ("\halign or \valign is being set up. In this case you had")@/
> ("more than one, so I'm ignoring all but the first.");
> error; goto continue;
> end;
> link(p):=get_avail; p:=link(p); info(p):=cur_tok;
> end;
> done2: link(p):=get_avail; p:=link(p);
> info(p):=end_template_token {put \.{\\endtemplate} at the end}
12562c15403,15404
< begin @<Get the next non-blank non-call token@>;
---
> label restart;
> begin restart: align_state:=1000000; @<Get the next non-blank non-call token@>;
12564,12565c15406,15408
< begin scan_left_brace; new_save_level(no_align_group);
< end
---
> begin scan_left_brace; new_save_level(no_align_group);
> if mode=-vmode then normal_paragraph;
> end
12567,12569c15410,15414
< else begin init_row; {start a new row}
< init_col; {start a new column and replace what we peeked at}
< end;
---
> else if (cur_cmd=car_ret)and(cur_chr=cr_cr_code) then
> goto restart {ignore \.{\\crcr}}
> else begin init_row; {start a new row}
> init_col; {start a new column and replace what we peeked at}
> end;
12574a15420,15421
> The |space_factor| and |prev_depth| are not used on this semantic level,
> but we clear them to zero just to be tidy.
12578c15425,15426
< begin push_nest; mode←(-hmode-vmode)-mode;
---
> begin push_nest; mode:=(-hmode-vmode)-mode;
> if mode=-hmode then space_factor:=0 @+else prev_depth:=0;
12580,12581c15428,15429
< subtype(tail)←tab_skip_code+1;@/
< cur_align←link(preamble); init_span(cur_align);
---
> subtype(tail):=tab_skip_code+1;@/
> cur_align:=link(preamble); cur_tail:=cur_head; init_span(cur_align);
12591,12593c15439,15442
< if mode=-vmode then prev_depth←ignore_depth
< else space_factor←1000;
< cur_span←p;
---
> if mode=-hmode then space_factor:=1000
> else begin prev_depth:=ignore_depth; normal_paragraph;
> end;
> cur_span:=p;
12597,12599c15446,15450
< the current token should be put back into the input until the \<u↓j> template
< has been scanned. We remain in the same mode, and start the template if
< it is called for.
---
> the current token should be put back into the input until the \<u_j>
> template has been scanned. (Note that |cur_cmd| might be |tab_mark| or
> |car_ret|.) We also assume that |align_state| is approximately 1000000 at
> this time. We remain in the same mode, and start the template if it is
> called for.
12602,12606c15453,15456
< begin extra_info(cur_align)←cur_cmd;
< if cur_cmd=omit then align_state←0
< else begin back_input; begin_token_list(u_part(cur_align),u_template);
< align_state←1000000;
< end;
---
> begin extra_info(cur_align):=cur_cmd;
> if cur_cmd=omit then align_state:=0
> else begin back_input; begin_token_list(u_part(cur_align),u_template);
> end; {now |align_state=1000000|}
12609c15459
< @ The scanner sets |align_state| to zero when the \<u↓j> template ends. When
---
> @ The scanner sets |align_state| to zero when the \<u_j> template ends. When
12611,12616c15461,15472
< the scanner activates the following code, which fires up the \<v↓j> template.
< We need to remember the |cur_chr|, which is either |cr_code|, |span_code|,
< or a character code, depending on how the column text has ended.
<
< @<Insert the \(v)\<v↓j>...@>=
< begin cur_cmd←extra_info(cur_align); extra_info(cur_align)←cur_chr;
---
> the scanner activates the following code, which fires up the \<v_j> template.
> We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|,
> |span_code|, or a character code, depending on how the column text has ended.
>
> This part of the program had better not be activated when the preamble
> to another alignment is being scanned, or when no alignment preamble is active.
>
> @<Insert the \(v)\<v_j>...@>=
> begin if (scanner_status=aligning) or (cur_align=null) then
> fatal_error("(interwoven alignment preambles are not allowed)");
> @.interwoven alignment preambles...@>
> cur_cmd:=extra_info(cur_align); extra_info(cur_align):=cur_chr;
12619c15475
< align_state←1000000; goto restart;
---
> align_state:=1000000; goto restart;
12623c15479
< list that contains |endv| only.
---
> list that contains the special control sequence \.{\\endtemplate} only.
12626c15482
< info(omit_template)←endv_token; {|link(omit_template)=null|}
---
> info(omit_template):=end_template_token; {|link(omit_template)=null|}
12628c15484
< @ When the |endv| token at the end of a \<v↓j> template comes through the
---
> @ When the |endv| command at the end of a \<v_j> template comes through the
12636c15492
< @!q:pointer; {runs through spanned alignrecords and span nodes}
---
> @!q,@!r:pointer; {temporary pointers for list manipulation}
12642,12644c15498,15504
< begin if (cur_align=null)∨(link(cur_align)=null) then confusion("endv");
< @:confusion endv}{\quad endv@>
< p←link(link(cur_align));
---
> begin if cur_align=null then confusion("endv");
> q:=link(cur_align);@+if q=null then confusion("endv");
> @:this can't happen endv}{\quad endv@>
> if align_state<500000 then
> fatal_error("(interwoven alignment preambles are not allowed)");
> @.interwoven alignment preambles...@>
> p:=link(q);
12646,12657c15506,15517
< if extra_info(cur_align)≠span_code then
< begin @<Package an unset box for the current column and
< record its width@>;
< @<Copy the tabskip glue between columns@>;
< if extra_info(cur_align)=cr_code then
< @<Package any omitted columns as dummies,
< then |return| with |fin_col=true|@>;
< init_span(p);
< end;
< @<Get the next non-blank non-call token@>;
< cur_align←p;
< init_col; fin_col←false;
---
> if extra_info(cur_align)<>span_code then
> begin unsave; new_save_level(align_group);@/
> @<Package an unset box for the current column and record its width@>;
> @<Copy the tabskip glue between columns@>;
> if extra_info(cur_align)>=cr_code then
> begin fin_col:=true; return;
> end;
> init_span(p);
> end;
> align_state:=1000000; @<Get the next non-blank non-call token@>;
> cur_align:=p;
> init_col; fin_col:=false;
12661,12662c15521,15523
< if (p=null)∧(extra_info(cur_align)≠cr_code) then
< begin print_nl("! Extra alignment tab has been changed to \cr");
---
> if (p=null)and(extra_info(cur_align)<cr_code) then
> if cur_loop<>null then @<Lengthen the preamble periodically@>
> else begin print_err("Extra alignment tab has been changed to ");
12664,12668c15525,15550
< help3("You have given more \span or & marks than there were")@/
< ("in the preamble to the \halign or \valign now in progress.")@/
< ("So I'll assume that you meant to type \cr instead.");
< extra_info(cur_align)←cr_code; error;
< end
---
> print_esc("cr");
> help3("You have given more \span or & marks than there were")@/
> ("in the preamble to the \halign or \valign now in progress.")@/
> ("So I'll assume that you meant to type \cr instead.");
> extra_info(cur_align):=cr_code; error;
> end
>
> @ @<Lengthen the preamble...@>=
> begin link(q):=new_null_box; p:=link(q); {a new alignrecord}
> info(p):=end_span; width(p):=null_flag; cur_loop:=link(cur_loop);
> @<Copy the templates from node |cur_loop| into node |p|@>;
> cur_loop:=link(cur_loop);
> link(p):=new_glue(glue_ptr(cur_loop));
> end
>
> @ @<Copy the templates from node |cur_loop| into node |p|@>=
> q:=hold_head; r:=u_part(cur_loop);
> while r<>null do
> begin link(q):=get_avail; q:=link(q); info(q):=info(r); r:=link(r);
> end;
> link(q):=null; u_part(p):=link(hold_head);
> q:=hold_head; r:=v_part(cur_loop);
> while r<>null do
> begin link(q):=get_avail; q:=link(q); info(q):=info(r); r:=link(r);
> end;
> link(q):=null; v_part(p):=link(hold_head)
12672,12684c15554
< subtype(tail)←tab_skip_code+1
<
< @ @<Package any omitted columns...@>=
< begin cur_align←p;
< while cur_align≠null do
< begin tail_append(new_null_box);
< type(tail)←unset_node;
< if width(cur_align)<0 then width(cur_align)←0;
< @<Copy the tabskip glue...@>;
< cur_align←link(link(cur_align));
< end;
< fin_col←true; return;
< end
---
> subtype(tail):=tab_skip_code+1
12688,12695c15558,15566
< begin u←hpack(head,natural); w←width(u);
< end
< else begin u←vpackage(head,natural,0); w←height(u);
< end;
< n←min_quarterword; {this represents a span count of 1}
< if cur_span≠cur_align then @<Update width entry for spanned columns@>
< else if w>width(cur_align) then width(cur_align)←w;
< type(u)←unset_node; span_count(u)←n;@/
---
> begin adjust_tail:=cur_tail; u:=hpack(link(head),natural); w:=width(u);
> cur_tail:=adjust_tail; adjust_tail:=null;
> end
> else begin u:=vpackage(link(head),natural,0); w:=height(u);
> end;
> n:=min_quarterword; {this represents a span count of 1}
> if cur_span<>cur_align then @<Update width entry for spanned columns@>
> else if w>width(cur_align) then width(cur_align):=w;
> type(u):=unset_node; span_count(u):=n;@/
12697c15568
< glue_order(u)←o; glue_stretch(u)←total_stretch[o];@/
---
> glue_order(u):=o; glue_stretch(u):=total_stretch[o];@/
12699,12700c15570,15571
< glue_sign(u)←o; glue_shrink(u)←total_shrink[o];@/
< pop_nest; link(tail)←u; tail←u;
---
> glue_sign(u):=o; glue_shrink(u):=total_shrink[o];@/
> pop_nest; link(tail):=u; tail:=u;
12703c15574
< @ A span node is a 2-word record contining |width|, |info|, and |link|
---
> @ A span node is a 2-word record containing |width|, |info|, and |link|
12717c15588
< link(end_span)←max_quarterword+1; info(end_span)←null;
---
> link(end_span):=max_quarterword+1; info(end_span):=null;
12720,12721c15591,15592
< q←cur_span;
< repeat incr(n); q←link(link(q));
---
> begin q:=cur_span;
> repeat incr(n); q:=link(link(q));
12724,12725c15595,15597
< @:confusion 256 spans}{\quad 256 spans@>
< q←cur_span; while link(info(q))<n do q←info(q);
---
> @^system dependencies@>
> @:this can't happen 256 spans}{\quad 256 spans@>
> q:=cur_span; while link(info(q))<n do q:=info(q);
12727,12730c15599,15603
< begin s←get_node(span_node_size); info(s)←info(q); link(s)←n;
< info(q)←s; width(s)←w;
< end
< else if width(info(q))<w then width(info(q))←w
---
> begin s:=get_node(span_node_size); info(s):=info(q); link(s):=n;
> info(q):=s; width(s):=w;
> end
> else if width(info(q))<w then width(info(q)):=w;
> end
12740,12745c15613,15623
< begin p←hpack(link(head),natural); pop_nest; append_to_vlist(p);
< end
< else begin p←vpack(link(head),natural); pop_nest;
< link(tail)←p; tail←p; space_factor←1000;
< end;
< type(p)←unset_node; glue_stretch(p)←0;
---
> begin p:=hpack(link(head),natural);
> pop_nest; append_to_vlist(p);
> if cur_head<>cur_tail then
> begin link(tail):=link(cur_head); tail:=cur_tail;
> end;
> end
> else begin p:=vpack(link(head),natural); pop_nest;
> link(tail):=p; tail:=p; space_factor:=1000;
> end;
> type(p):=unset_node; glue_stretch(p):=0;
> if every_cr<>null then begin_token_list(every_cr,every_cr_text);
12747c15625
< end;
---
> end; {note that |glue_shrink(p)=0| since |glue_shrink==shift_amount|}
12757,12758c15635,15637
< var @!p,@!q,@!r,@!s,@!u: pointer; {registers for the list operations}
< @!t:scaled; {width of column plus tabskip}
---
> var @!p,@!q,@!r,@!s,@!u,@!v: pointer; {registers for the list operations}
> @!t,@!w:scaled; {width of column}
> @!o:scaled; {shift offset for unset boxes}
12760,12762c15639,15647
< begin if cur_group≠align_group then confusion("align");
< @:confusion align}{\quad align@>
< unsave;@/
---
> @!rule_save:scaled; {temporary storage for |overfull_rule|}
> @!aux_save:memory_word; {temporary storage for |aux|}
> begin if cur_group<>align_group then confusion("align1");
> @:this can't happen align}{\quad align@>
> unsave; {that |align_group| was for individual entries}
> if cur_group<>align_group then confusion("align0");
> unsave; {that |align_group| was for the whole alignment}
> if nest[nest_ptr-1].mode_field=mmode then o:=display_indent
> else o:=0;
12764c15649
< changing the alignrecords to dummy unset boxes@>;
---
> changing the alignrecords to dummy unset boxes@>;
12766c15651
< and let |p| point to this prototype box@>;
---
> and let |p| point to this prototype box@>;
12771c15656
< @<Declare the procedure called |align_peek|@>
---
> @t\4@>@<Declare the procedure called |align_peek|@>
12774,12779c15659,15664
< widths. Let $w↓{ij}$ be the maximum of the natural widths of all entries
< that span columns $i$ through $j$, inclusive. The alignrecord for column@@$i$
< contains $w↓{ii}$ in its |width| field, and there is also a linked list of
< the nonzero $w↓{ij}$ for increasing $j$, accessible via the |info| field;
< these span nodes contain the value $j-i-1+|min_quarterword|$ in their
< |link| fields. The values of $w↓{ii}$ were initialized to |null_flag|, which
---
> widths. Let $w_{ij}$ be the maximum of the natural widths of all entries
> that span columns $i$ through $j$, inclusive. The alignrecord for column~$i$
> contains $w_{ii}$ in its |width| field, and there is also a linked list of
> the nonzero $w_{ij}$ for increasing $j$, accessible via the |info| field;
> these span nodes contain the value $j-i+|min_quarterword|$ in their
> |link| fields. The values of $w_{ii}$ were initialized to |null_flag|, which
12783,12793c15668,15679
< $$w↓j=\max↓{1\L i\L j}\bigglp(w↓{ij}-\sum↓{i\L k<j}(t↓k+w↓k)\biggrp,$$
< where $t↓k$ is the natural width of the tabskip glue between columns
< $k$ and@@$k+1$. However, if $w↓{ij}=-\infty$ for all |i| in the range
< |1≤i≤j| (i.e., if every entry that involved column@@|j| also involved
< column@@|j+1|), we let $w↓j=0$.
<
< \TeX\ computes these values by using the following scheme: First $w↓1=w↓{11}$.
< Then replace $w↓{2j}$ by $\max(w↓{2j},w↓{1j}-t↓1-w↓1)$, for all $j>1$.
< Then $w↓2=w↓{22}$. Then replace $w↓{3j}$ by $\max(w↓{3j},w↓{2j}-t↓2-w↓2)$
< for all $j>2$; and so on. If any $w↓j$ turns out to be $-\infty$, its
< value is changed to zero.
---
> $$w_j=\max_{1\L i\L j}\biggl( w_{ij}-\sum_{i\L k<j}(t_k+w_k)\biggr),$$
> where $t_k$ is the natural width of the tabskip glue between columns
> $k$ and~$k+1$. However, if $w_{ij}=-\infty$ for all |i| in the range
> |1<=i<=j| (i.e., if every entry that involved column~|j| also involved
> column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after
> column~|j|.
>
> \TeX\ computes these values by using the following scheme: First $w_1=w_{11}$.
> Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, for all $j>1$.
> Then $w_2=w_{22}$. Then replace $w_{3j}$ by $\max(w_{3j},w_{2j}-t_2-w_2)$
> for all $j>2$; and so on. If any $w_j$ turns out to be $-\infty$, its
> value is changed to zero and so is the next tabskip.
12796c15682
< q←link(preamble);
---
> q:=link(preamble);
12798,12804c15684,15692
< p←link(link(q));
< if width(q)=null_flag then width(q)←0;
< if (p≠null)∧(info(q)≠end_span) then
< @<Merge the widths in the span nodes of |q| with those of |p|,
< destroying the span nodes of |q|@>;
< type(q)←unset_node; span_count(q)←min_quarterword; height(q)←width(q);
< depth(q)←0; glue_order(q)←normal; glue_sign(q)←normal; q←p;
---
> p:=link(link(q));
> if width(q)=null_flag then
> @<Nullify |width(q)| and the tabskip glue following this column@>;
> if info(q)<>end_span then
> @<Merge the widths in the span nodes of |q| with those of |p|,
> destroying the span nodes of |q|@>;
> type(q):=unset_node; span_count(q):=min_quarterword; height(q):=0;
> depth(q):=0; glue_order(q):=normal; glue_sign(q):=normal;
> glue_stretch(q):=0; glue_shrink(q):=0; q:=p;
12806a15695,15702
> @ @<Nullify |width(q)| and the tabskip glue following this column@>=
> begin width(q):=0; r:=link(q); s:=glue_ptr(r);
> if s<>zero_glue then
> begin add_glue_ref(zero_glue); delete_glue_ref(s);
> glue_ptr(r):=zero_glue;
> end;
> end
>
12815,12817c15711,15713
< begin t←width(q)+width(glue_ptr(link(q)));
< r←info(q); s←end_span; info(s)←p; n←min_quarterword+1;
< repeat width(r)←width(r)-t; u←info(r);
---
> begin t:=width(q)+width(glue_ptr(link(q)));
> r:=info(q); s:=end_span; info(s):=p; n:=min_quarterword+1;
> repeat width(r):=width(r)-t; u:=info(r);
12819,12820c15715,15716
< begin s←info(s); n←link(info(s))+1;
< end;
---
> begin s:=info(s); n:=link(info(s))+1;
> end;
12822,12827c15718,15723
< begin info(r)←info(s); info(s)←r; decr(link(r)); s←r;
< end
< else begin if width(r)>width(info(s)) then width(info(s))←width(r);
< free_node(r,span_node_size);
< end;
< r←u;
---
> begin info(r):=info(s); info(s):=r; decr(link(r)); s:=r;
> end
> else begin if width(r)>width(info(s)) then width(info(s)):=width(r);
> free_node(r,span_node_size);
> end;
> r:=u;
12832,12837c15728,15731
< boxes and tabskip glue, where the box heights and widths are both equal
< to the final column sizes. This list is both an hlist and a vlist, and
< both |hpack| and |vpack| will set its glue in the same way; but we should
< choose the correct alternative, because it is important to produce the
< correct error messages when an alignment is overfull or underfull.
< The following program changes the tabskip amounts to equivalent kerns.
---
> boxes and tabskip glue, where the box widths are equal to the final
> column sizes. In case of \.{\\valign}, we change the widths to heights,
> so that a correct error message will be produced if the alignment is
> overfull or underfull.
12840,12853c15734,15748
< save_ptr←save_ptr-2;
< if mode=-hmode then
< p←hpack(preamble,saved(1),saved(0))
< else p←vpack(preamble,saved(1),saved(0));
< q←p+list_offset;
< repeat q←link(q); r←glue_ptr(q); type(q)←kern_node; subtype(q)←normal;
< if glue_sign(p)=normal then width(q)←width(r)
< else if glue_sign(p)=stretching then
< if stretch_order(r)≠glue_order(p) then width(q)←width(r)
< else width(q)←width(q)+round(glue_set(p)*stretch(r))
< else if shrink_order(r)≠glue_order(p) then width(q)←width(r)
< else width(q)←width(q)-round(glue_set(p)*shrink(r));
< delete_glue_ref(r); q←link(q);
< until q=null
---
> save_ptr:=save_ptr-2; pack_begin_line:=-mode_line;
> if mode=-vmode then
> begin rule_save:=overfull_rule;
> overfull_rule:=0; {prevent rule from being packaged}
> p:=hpack(preamble,saved(1),saved(0)); overfull_rule:=rule_save;
> end
> else begin q:=link(preamble);
> repeat height(q):=width(q); width(q):=0; q:=link(link(q));
> until q=null;
> p:=vpack(preamble,saved(1),saved(0));
> q:=link(preamble);
> repeat width(q):=height(q); height(q):=0; q:=link(link(q));
> until q=null;
> end;
> pack_begin_line:=0
12856,12861c15751,15773
< q←link(head);
< while q≠null do
< begin if type(q)=unset_node then
< @<Set the unset box |q| and the unset boxes in it@>;
< q←link(q);
< end
---
> q:=link(head); s:=head;
> while q<>null do
> begin if not is_char_node(q) then
> if type(q)=unset_node then
> @<Set the unset box |q| and the unset boxes in it@>
> else if type(q)=rule_node then
> @<Make the running dimensions in rule |q| extend to the
> boundaries of the alignment@>;
> s:=q; q:=link(q);
> end
>
> @ @<Make the running dimensions in rule |q| extend...@>=
> begin if is_running(width(q)) then width(q):=width(p);
> if is_running(height(q)) then height(q):=height(p);
> if is_running(depth(q)) then depth(q):=depth(p);
> if o<>0 then
> begin r:=link(q); link(q):=null; q:=hpack(q,natural);
> shift_amount(q):=o; link(q):=r; link(s):=q;
> end;
> end
>
> @ The unset box |q| represents a row that contains one or more unset boxes,
> depending on how soon \.{\\cr} occurred in that row.
12863c15775
< @ @<Set the unset box |q| and the unset boxes in it@>=
---
> @<Set the unset box |q| and the unset boxes in it@>=
12865,12871c15777,15783
< begin type(q)←hlist_node; width(q)←width(p);
< end
< else begin type(q)←vlist_node; height(q)←height(p);
< end;
< glue_order(q)←glue_order(p); glue_sign(q)←glue_sign(p);
< glue_set(q)←glue_set(p); shift_amount(q)←0;@/
< r←link(list_ptr(q)); s←link(list_ptr(p));
---
> begin type(q):=hlist_node; width(q):=width(p);
> end
> else begin type(q):=vlist_node; height(q):=height(p);
> end;
> glue_order(q):=glue_order(p); glue_sign(q):=glue_sign(p);
> glue_set(q):=glue_set(p); shift_amount(q):=o;
> r:=link(list_ptr(q)); s:=link(list_ptr(p));
12873c15785
< r←link(link(r)); s←link(link(s));
---
> r:=link(link(r)); s:=link(link(s));
12877,12878c15789,15795
< @ @<Set the glue in node |r|...@>=
< n←span_count(r); t←width(s);
---
> @ A box made from spanned columns will be followed by tabskip glue nodes and
> by empty boxes as if there were no spanning. This permits perfect alignment
> of subsequent entries, and it prevents values that depend on floating point
> arithmetic from entering into the dimensions of any boxes.
>
> @<Set the glue in node |r|...@>=
> n:=span_count(r); t:=width(s); w:=t; u:=hold_head;
12880,12882c15797,15800
< begin decr(n); s←link(s); t←t+width(s);
< s←link(s); t←t+width(s);
< end;
---
> begin decr(n);
> @<Append tabskip glue and an empty box to list |u|,
> and update |s| and |t| as the prototype nodes are passed@>;
> end;
12884,12886c15802,15826
< @<Make the unset node |r| into an |hlist_node| of width |t|@>
< else @<Make the unset node |r| into a |vlist_node| of height |t|@>;
< shift_amount(r)←0;
---
> @<Make the unset node |r| into an |hlist_node| of width |w|,
> setting the glue as if the width were |t|@>
> else @<Make the unset node |r| into a |vlist_node| of height |w|,
> setting the glue as if the height were |t|@>;
> shift_amount(r):=0;
> if u<>hold_head then {append blank boxes to account for spanned nodes}
> begin link(u):=link(r); link(r):=link(hold_head); r:=u;
> end
>
> @ @<Append tabskip glue and an empty box to list |u|...@>=
> s:=link(s); v:=glue_ptr(s); link(u):=new_glue(v); u:=link(u);
> subtype(u):=tab_skip_code+1; t:=t+width(v);
> if glue_sign(p)=stretching then
> begin if stretch_order(v)=glue_order(p) then
> t:=t+round(float(glue_set(p))*stretch(v));
> @^real multiplication@>
> end
> else if glue_sign(p)=shrinking then
> begin if shrink_order(v)=glue_order(p) then
> t:=t-round(float(glue_set(p))*shrink(v));
> end;
> s:=link(s); link(u):=new_null_box; u:=link(u); t:=t+width(s);
> if mode=-vmode then width(u):=width(s)@+else
> begin type(u):=vlist_node; height(u):=width(s);
> end
12888,12889c15828,15829
< @ @<Make the unset node |r| into an |hlist_node| of width |t|@>=
< begin height(r)←height(q); depth(r)←depth(q);
---
> @ @<Make the unset node |r| into an |hlist_node| of width |w|...@>=
> begin height(r):=height(q); depth(r):=depth(q);
12891,12893c15831,15833
< begin glue_sign(r)←normal; glue_order(r)←normal;
< glue_set(r)←0.0;
< end
---
> begin glue_sign(r):=normal; glue_order(r):=normal;
> set_glue_ratio_zero(glue_set(r));
> end
12895,12903c15835,15846
< begin glue_sign(r)←stretching;
< if glue_stretch(r)=0 then glue_set(r)←0.0
< else glue_set(r)←(t-width(r))/glue_stretch(r);
< end
< else begin glue_order(r)←glue_sign(r); glue_sign(r)←shrinking;
< if glue_shrink(r)=0 then glue_set(r)←0.0
< else glue_set(r)←(width(r)-t)/glue_shrink(r);
< end;
< width(r)←t; type(r)←hlist_node;
---
> begin glue_sign(r):=stretching;
> if glue_stretch(r)=0 then set_glue_ratio_zero(glue_set(r))
> else glue_set(r):=unfloat((t-width(r))/glue_stretch(r));
> @^real division@>
> end
> else begin glue_order(r):=glue_sign(r); glue_sign(r):=shrinking;
> if glue_shrink(r)=0 then set_glue_ratio_zero(glue_set(r))
> else if (glue_order(r)=normal)and(width(r)-t>glue_shrink(r)) then
> set_glue_ratio_one(glue_set(r))
> else glue_set(r):=unfloat((width(r)-t)/glue_shrink(r));
> end;
> width(r):=w; type(r):=hlist_node;
12906,12907c15849,15850
< @ @<Make the unset node |r| into a |vlist_node| of height |t|@>=
< begin width(r)←width(q);
---
> @ @<Make the unset node |r| into a |vlist_node| of height |w|...@>=
> begin width(r):=width(q);
12909,12911c15852,15854
< begin glue_sign(r)←normal; glue_order(r)←normal;
< glue_set(r)←0.0;
< end
---
> begin glue_sign(r):=normal; glue_order(r):=normal;
> set_glue_ratio_zero(glue_set(r));
> end
12913,12921c15856,15867
< begin glue_sign(r)←stretching;
< if glue_stretch(r)=0 then glue_set(r)←0.0
< else glue_set(r)←(t-height(r))/glue_stretch(r);
< end
< else begin glue_order(r)←glue_sign(r); glue_sign(r)←shrinking;
< if glue_shrink(r)=0 then glue_set(r)←0.0
< else glue_set(r)←(height(r)-t)/glue_shrink(r);
< end;
< height(r)←t; type(r)←vlist_node;
---
> begin glue_sign(r):=stretching;
> if glue_stretch(r)=0 then set_glue_ratio_zero(glue_set(r))
> else glue_set(r):=unfloat((t-height(r))/glue_stretch(r));
> @^real division@>
> end
> else begin glue_order(r):=glue_sign(r); glue_sign(r):=shrinking;
> if glue_shrink(r)=0 then set_glue_ratio_zero(glue_set(r))
> else if (glue_order(r)=normal)and(height(r)-t>glue_shrink(r)) then
> set_glue_ratio_one(glue_set(r))
> else glue_set(r):=unfloat((height(r)-t)/glue_shrink(r));
> end;
> height(r):=w; type(r):=vlist_node;
12927c15873,15878
< another routine will take care of inserting glue before and after the display.)
---
> we will need to insert glue before and after the display; that part of the
> program will be deferred until we're more familiar with such operations.)
>
> In restricted horizontal mode, the |clang| part of |aux| is undefined;
> an over-cautious \PASCAL\ runtime system may complain about this.
> @^dirty \PASCAL@>
12930c15881
< t←aux; p←link(head); q←tail; pop_nest;
---
> aux_save:=aux; p:=link(head); q:=tail; pop_nest;
12932,12936c15883,15888
< else begin aux←t; link(tail)←p;
< if q≠null then tail←q;
< if mode=vmode then build_page;
< end
< @* \[37] Breaking paragraphs into lines.
---
> else begin aux:=aux_save; link(tail):=p;
> if p<>null then tail:=q;
> if mode=vmode then build_page;
> end
>
> @* \[38] Breaking paragraphs into lines.
12957c15909
< new ideas to the algorithm of 1980: memory space requirements are considerably
---
> new ideas to the algorithm of 1980: Memory space requirements are considerably
12970c15922
< starts at |link(head)|, and it is nonempty. The value of |already| in the
---
> starts at |link(head)|, and it is nonempty. The value of |prev_graf| in the
12973c15925
< are in use; |already| is zero unless this paragraph is being continued
---
> is in use; |prev_graf| is zero unless this paragraph is being continued
12979c15931
< value of |already|. Furthermore, the global variable |just_box| will
---
> value of |prev_graf|. Furthermore, the global variable |just_box| will
12982c15934
< |disp_skip| or |disp_a_skip| before a displayed formula.
---
> |above_display_skip| or |above_display_short_skip| before a displayed formula.
12993,12996c15945,15948
< procedure line_break(@!final_widow_penalty:integer);
< label done,done1,done2,done3,done4;
< var@?@<Local variables for line breaking@>@;
< begin par_begin_line←mode_line; {this is for over/underfull box messages}
---
> procedure line_break(@!final_widow_penalty:integer);
> label done,done1,done2,done3,done4,done5,continue;
> var @<Local variables for line breaking@>@;
> begin pack_begin_line:=mode_line; {this is for over/underfull box messages}
13002c15954
< par_begin_line←0;
---
> pack_begin_line:=0;
13013c15965,15966
< same number of words in |mem|.
---
> same number of |mem|~words.
> @^data structure assumptions@>
13015,13016c15968,15969
< @<Get ready...@>=
< link(temp_head)←link(head);
---
> @<Get ready to start...@>=
> link(temp_head):=link(head);
13018,13023c15971,15978
< else if (type(tail)≠glue_node)∨(subtype(tail)≥a_leaders) then
< tail_append(new_penalty(inf_penalty))
< else begin type(tail)←penalty_node; delete_glue_ref(glue_ptr(tail));
< penalty(tail)←inf_penalty;
< end;
< link(tail)←new_param_glue(par_fill_skip_code);
---
> else if type(tail)<>glue_node then tail_append(new_penalty(inf_penalty))
> else begin type(tail):=penalty_node; delete_glue_ref(glue_ptr(tail));
> flush_node_list(leader_ptr(tail)); penalty(tail):=inf_penalty;
> end;
> link(tail):=new_param_glue(par_fill_skip_code);
> init_cur_lang:=prev_graf mod @'200000;
> init_l_hyf:=prev_graf div @'20000000;
> init_r_hyf:=(prev_graf div @'200000) mod @'100;
13035,13041c15990,15996
< @d tight_fit=0 {fitness classification for lines shrinking 0.5 to 1.0 of their
< shrinkability}
< @d loose_fit=2 {fitness classification for lines stretching 0.5 to 1.0 of their
< stretchability}
< @d very_loose_fit=3 {fitness classification for lines stretching more than
< their stretchability}
< @d decent_fit=1 {fitness classification for all other lines}
---
> @d tight_fit=3 {fitness classification for lines shrinking 0.5 to 1.0 of their
> shrinkability}
> @d loose_fit=1 {fitness classification for lines stretching 0.5 to 1.0 of their
> stretchability}
> @d very_loose_fit=0 {fitness classification for lines stretching more than
> their stretchability}
> @d decent_fit=2 {fitness classification for all other lines}
13073c16028
< \yskip\hang|type| is either |hyphenated| or |unhyphenated|, de\-pending on
---
> \yskip\hang|type| is either |hyphenated| or |unhyphenated|, depending on
13086c16041
< @d fitness==subtype {|tight_fit..very_loose_fit| on final line for this break}
---
> @d fitness==subtype {|very_loose_fit..tight_fit| on final line for this break}
13095,13096c16050,16051
< type(last_active)←hyphenated; line_number(last_active)←max_halfword;
< subtype(last_active)←0; {the |subtype| is never examined by the algorithm}
---
> type(last_active):=hyphenated; line_number(last_active):=max_halfword;
> subtype(last_active):=0; {the |subtype| is never examined by the algorithm}
13098c16053
< @ The passive node for a given breakpoint contains only three fields:
---
> @ The passive node for a given breakpoint contains only four fields:
13108a16064,16067
> \yskip\hang|serial| is equal to |n| if this passive node is the |n|th
> one created during the current pass. (This field is used only when
> printing out detailed statistics about the line-breaking calculations.)
>
13111c16070,16072
< recently created passive node.
---
> recently created passive node. Another global variable, |printed_node|,
> is used to help print out the paragraph when detailed information about
> the line-breaking computation is being displayed.
13114,13115c16075,16077
< @d cur_break==mark_ptr {in passive node, points to position of this breakpoint}
< @d prev_break==info {points to passive node that should precede this one}
---
> @d cur_break==rlink {in passive node, points to position of this breakpoint}
> @d prev_break==llink {points to passive node that should precede this one}
> @d serial==info {serial number for symbolic identification}
13118a16081,16082
> @!printed_node:pointer; {most recent node that has been printed}
> @!pass_number:halfword; {the number of passive nodes allocated on this pass}
13122c16086
< active nodes, and they have |type=delta|. If |p| and |r| are active nodes
---
> active nodes, and they have |type=delta_node|. If |p| and |r| are active nodes
13128c16092
< starts after |r| is obtained by adding the amounts in node@@|q|. A delta node
---
> starts after |r| is obtained by adding the amounts in node~|q|. A delta node
13146,13149c16110,16114
< When we pass a delta node we want to do operations like `\!|for k←1 to 6 do
< cur_active_width[k]←cur_active_width[k]+mem[q+k].sc|', and we want to
< do this without the overhead of |for| loops. The |do_all_six| macro
< makes such six-tuples convenient.
---
> When we pass a delta node we want to do operations like
> $$\hbox{\ignorespaces|for
> k:=1 to 6 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we
> want to do this without the overhead of |for| loops. The |do_all_six|
> macro makes such six-tuples convenient.
13153c16118
< @<Glo...@>=
---
> @<Glob...@>=
13155c16120
< {distance from first active node to@@|cur_p|}
---
> {distance from first active node to~|cur_p|}
13162,13164c16127,16129
< breakpoint@@$p$ in the paragraph, we define two quantities $\alpha(p)$ and
< $\beta(p)$ such that the length of material in a line from breakpoint@@$p$
< to breakpoint@@$q$ is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$.
---
> breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and
> $\beta(p)$ such that the length of material in a line from breakpoint~|p|
> to breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$.
13166,13167c16131,16132
< the beginning of the paragraph to a point ``after'' a break at $p$ and to a
< point ``before'' a break at $q$; and $\gamma$ is the width of an empty line,
---
> the beginning of the paragraph to a point ``after'' a break at |p| and to a
> point ``before'' a break at |q|; and $\gamma$ is the width of an empty line,
13171,13177c16136,16142
< boxes and glue skips; let the boxes have widths $x↓1\ldotsm x↓n$ and
< let the skips have widths $y↓1\ldotsm y↓n$, so that the paragraph can be
< represented by $x↓1y↓1\ldotsm x↓ny↓n$. Let $p↓i$ be the legal breakpoint
< at $y↓i$; then $\alpha(p↓i)=x↓1+y↓1+\cdots+x↓i+y↓i$, and $\beta(p↓i)=
< x↓1+y↓1+\cdots+x↓i$. To check this, note that the length of material from
< $p↓2$ to $p↓5$, say, is $\gamma+x↓3+y↓3+x↓4+y↓4+x↓5=\gamma+\beta(p↓5)
< -\alpha(p↓2)$.
---
> boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and
> let the skips have widths $y_1\ldots y_n$, so that the paragraph can be
> represented by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint
> at $y_i$; then $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)=
> x_1+y_1+\cdots+x_i$. To check this, note that the length of material from
> $p_2$ to $p_5$, say, is $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5)
> -\alpha(p_2)$.
13181c16146
< and $\beta(p)$ for each $p$, we would need multiple precision arithmetic, and
---
> and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and
13185,13189c16150,16154
< $a↓1\,\delta↓1\,a↓2\,\delta↓2\,a↓3$, where the $a$'s are active breakpoints
< and the $\delta$'s are delta nodes. Then $\delta↓1=\alpha(a↓1)-\alpha(a↓2)$
< and $\delta↓2=\alpha(a↓2)-\alpha(a↓3)$. If the line breaking algorithm is
< currently positioned at some other breakpoint $p$, the |active_width| array
< contains the value $\gamma+\beta(p)-\alpha(a↓1)$. If we are scanning through
---
> $a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints
> and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$
> and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is
> currently positioned at some other breakpoint |p|, the |active_width| array
> contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through
13191,13195c16156,16160
< $a↓2$ to@@$p$, say, the |cur_active_width| array will contain the value
< $\gamma+\beta(p)-\alpha(a↓2)$. Thus, when we move from $a↓2$ to $a↓3$,
< we want to add $\alpha(a↓2)-\alpha(a↓3)$ to |cur_active_width|; and this
< is just $\delta↓2$, which appears in the active list between $a↓2$ and
< $a↓3$. The |background| array contains $\gamma$. The |break_width| array
---
> $a_2$ to~|p|, say, the |cur_active_width| array will contain the value
> $\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$,
> we want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this
> is just $\delta_2$, which appears in the active list between $a_2$ and
> $a_3$. The |background| array contains $\gamma$. The |break_width| array
13206,13208c16171,16173
< @d check_shrinkage(#)==if (shrink_order(#)≠normal)∧(shrink(#)≠0) then
< begin #←finite_shrink(#);
< end
---
> @d check_shrinkage(#)==if (shrink_order(#)<>normal)and(shrink(#)<>0) then
> begin #:=finite_shrink(#);
> end
13217,13223c16182,16183
< begin no_shrink_error_yet←false;
< help5("The paragraph just ended includes some glue that has")@/
< ("infinite shrinkability, e.g., `\hskip 0pt minus 1fil'.")@/
< ("Such glue doesn't belong there---it allows a paragraph")@/
< ("of any length to fit on one line. But it's safe to proceed,")@/
< ("since the offensive shrinkability has been made finite.");
< print_nl("! Infinite glue shrinkage found in a paragraph"); error;
---
> begin no_shrink_error_yet:=false;
> print_err("Infinite glue shrinkage found in a paragraph");
13225,13227c16185,16193
< end;
< q←new_spec(p); shrink_order(q)←normal;
< delete_glue_ref(p); finite_shrink←q;
---
> help5("The paragraph just ended includes some glue that has")@/
> ("infinite shrinkability, e.g., `\hskip 0pt minus 1fil'.")@/
> ("Such glue doesn't belong there---it allows a paragraph")@/
> ("of any length to fit on one line. But it's safe to proceed,")@/
> ("since the offensive shrinkability has been made finite.");
> error;
> end;
> q:=new_spec(p); shrink_order(q):=normal;
> delete_glue_ref(p); finite_shrink:=q;
13230,13231c16196,16197
< @ @<Get ready...@>=
< no_shrink_error_yet←true;@/
---
> @ @<Get ready to start...@>=
> no_shrink_error_yet:=true;@/
13233,13237c16199,16203
< q←left_skip; r←right_skip; background[1]←width(q)+width(r);@/
< background[2]←0; background[3]←0; background[4]←0; background[5]←0;@/
< background[2+stretch_order(q)]←stretch(q);@/
< background[2+stretch_order(r)]←@|background[2+stretch_order(r)]+stretch(r);@/
< background[6]←shrink(q)+shrink(r);
---
> q:=left_skip; r:=right_skip; background[1]:=width(q)+width(r);@/
> background[2]:=0; background[3]:=0; background[4]:=0; background[5]:=0;@/
> background[2+stretch_order(q)]:=stretch(q);@/
> background[2+stretch_order(r)]:=@|background[2+stretch_order(r)]+stretch(r);@/
> background[6]:=shrink(q)+shrink(r);
13244c16210
< of individual lines: breakpoints are feasible if there is a way to reach
---
> of individual lines: Breakpoints are feasible if there is a way to reach
13250c16216
< |badness| function specified above never returns a value greater than@@10000.
---
> |badness| function specified above never returns a value greater than~10000.
13252c16218
< Two passes might be made through the paragraph in an attempt to find at
---
> Up to three passes might be made through the paragraph in an attempt to find at
13254c16220,16221
< |threshold=pretolerance| and |second_pass=false|. If this pass fails to find a
---
> |threshold=pretolerance| and |second_pass=final_pass=false|.
> If this pass fails to find a
13256a16224,16225
> If that fails too, we add |emergency_stretch| to the background
> stretchability and set |final_pass=true|.
13260a16230
> @!final_pass:boolean; {is this our final attempt to break this paragraph?}
13266c16236
< nodes to@@|cur_p|. If feasible breaks are possible, new break nodes are
---
> nodes to~|cur_p|. If feasible breaks are possible, new break nodes are
13275,13276c16245,16246
< de\-pending on whether or not the current break is at a |disc_node|. The
< end of a para\-graph is also regarded as `|hyphenated|'; this case is
---
> depending on whether or not the current break is at a |disc_node|. The
> end of a paragraph is also regarded as `|hyphenated|'; this case is
13279c16249
< @d copy_to_cur_active(#)==cur_active_width[#]←active_width[#]
---
> @d copy_to_cur_active(#)==cur_active_width[#]:=active_width[#]
13284c16254
< label exit,done,continue,deactivate;
---
> label exit,done,done1,continue,deactivate;
13291c16261
< no_break_yet←true; prev_r←active; old_l←0;
---
> no_break_yet:=true; prev_r:=active; old_l:=0;
13293,13304c16263,16276
< loop@+ begin continue: r←link(prev_r);
< @<If node |r| is of type |delta_node|, update |cur_active_width|,
< set |prev_r| and |prev_prev_r|, then |goto continue|@>;
< @<If a line number class has ended, create new active nodes for
< the best feasible breaks in that class; then |return|
< if |r=last_active|, otherwise compute the new |line_width|@>;
< @<Consider the demerits for a line from |r| to |cur_p|;
< deactivate node |r| if it should no longer be active;
< then |goto continue| if a line from |r| to |cur_p| is infeasible,
< otherwise record a new feasible break@>;
< end;
< exit:end;
---
> loop@+ begin continue: r:=link(prev_r);
> @<If node |r| is of type |delta_node|, update |cur_active_width|,
> set |prev_r| and |prev_prev_r|, then |goto continue|@>;
> @<If a line number class has ended, create new active nodes for
> the best feasible breaks in that class; then |return|
> if |r=last_active|, otherwise compute the new |line_width|@>;
> @<Consider the demerits for a line from |r| to |cur_p|;
> deactivate node |r| if it should no longer be active;
> then |goto continue| if a line from |r| to |cur_p| is infeasible,
> otherwise record a new feasible break@>;
> end;
> exit: @!stat @<Update the value of |printed_node| for
> symbolic displays@>@+tats@;
> end;
13310,13311c16282,16283
< @!v:pointer; {points to a glue specification}
< @!t:quarterword; {replacement count, if |cur_p| is a discretionary node}
---
> @!v:pointer; {points to a glue specification or a node ahead of |cur_p|}
> @!t:integer; {node count, if |cur_p| is a discretionary node}
13316c16288
< @!fit_class:tight_fit..very_loose_fit; {possible fitness class of test line}
---
> @!fit_class:very_loose_fit..tight_fit; {possible fitness class of test line}
13318a16291,16293
> @!artificial_demerits:boolean; {has |d| been forced to zero?}
> @!save_link:pointer; {temporarily holds value of |link(cur_p)|}
> @!shortfall:scaled; {used in badness calculations}
13321,13323c16296,16298
< if abs(pi)≥inf_penalty then
< if pi>0 then return {this breakpoint is inhibited by infinite penalty}
< else pi←eject_penalty {this breakpoint will be forced}
---
> if abs(pi)>=inf_penalty then
> if pi>0 then return {this breakpoint is inhibited by infinite penalty}
> else pi:=eject_penalty {this breakpoint will be forced}
13325c16300
< @ The following code uses the fact that |type(last_active)≠delta_node|.
---
> @ The following code uses the fact that |type(last_active)<>delta_node|.
13328c16303
< cur_active_width[#]←cur_active_width[#]+mem[r+#].sc
---
> cur_active_width[#]:=cur_active_width[#]+mem[r+#].sc
13330a16306
> @^inner loop@>
13332,13334c16308,16310
< begin do_all_six(update_width);
< prev_prev_r←prev_r; prev_r←r; goto continue;
< end
---
> begin do_all_six(update_width);
> prev_prev_r:=prev_r; prev_r:=r; goto continue;
> end
13341c16317
< points to the passive node for the break before@@|cur_p| that achieves such
---
> points to the passive node for the break before~|cur_p| that achieves such
13351,13365c16327,16341
< @!minimal_demerits:array[tight_fit..very_loose_fit] of scaled; {best total
< demerits known for current line class and position, given the fitness}
< @!minimum_demerits:scaled; {best total demerits known for current line class
< and position}
< @!best_place:array[tight_fit..very_loose_fit] of pointer; {how to achieve
< |minimal_demerits|}
< @!best_pl_line:array[tight_fit..very_loose_fit] of halfword; {corresponding
< line number}
<
< @ @<Get ready...@>=
< minimum_demerits←awful_bad;
< minimal_demerits[tight_fit]←awful_bad;
< minimal_demerits[decent_fit]←awful_bad;
< minimal_demerits[loose_fit]←awful_bad;
< minimal_demerits[very_loose_fit]←awful_bad;
---
> @!minimal_demerits:array[very_loose_fit..tight_fit] of integer; {best total
> demerits known for current line class and position, given the fitness}
> @!minimum_demerits:integer; {best total demerits known for current line class
> and position}
> @!best_place:array[very_loose_fit..tight_fit] of pointer; {how to achieve
> |minimal_demerits|}
> @!best_pl_line:array[very_loose_fit..tight_fit] of halfword; {corresponding
> line number}
>
> @ @<Get ready to start...@>=
> minimum_demerits:=awful_bad;
> minimal_demerits[tight_fit]:=awful_bad;
> minimal_demerits[decent_fit]:=awful_bad;
> minimal_demerits[loose_fit]:=awful_bad;
> minimal_demerits[very_loose_fit]:=awful_bad;
13375c16351
< begin l←line_number(r);
---
> begin l:=line_number(r);
13377,13384c16353,16360
< begin {now we are no longer in the inner loop}
< if (minimum_demerits<awful_bad)∧@|
< ((old_l≠easy_line)∨(r=last_active)) then
< @<Create new active nodes for the best feasible breaks
< just found@>;
< if r=last_active then return;
< @<Compute the new line width@>;
< end;
---
> begin {now we are no longer in the inner loop}
> if (minimum_demerits<awful_bad)and@|
> ((old_l<>easy_line)or(r=last_active)) then
> @<Create new active nodes for the best feasible breaks
> just found@>;
> if r=last_active then return;
> @<Compute the new line width@>;
> end;
13387,13388c16363,16365
< @ It is not necessary to create new active nodes having |minimal_demerits
< >minimum_demerits+abs(adj_demerits)|, since such active nodes will never
---
> @ It is not necessary to create new active nodes having |minimal_demerits|
> greater than
> |minimum_demerits+abs(adj_demerits)|, since such active nodes will never
13395,13402c16372,16381
< minimum_demerits←minimum_demerits+abs(adj_demerits);
< for fit_class←tight_fit to very_loose_fit do
< begin if minimal_demerits[fit_class]≤minimum_demerits then
< @<Insert a new active node
< from |best_place[fit_class]| to |cur_p|@>;
< minimal_demerits[fit_class]←awful_bad;
< end;
< minimum_demerits←awful_bad;
---
> if abs(adj_demerits)>=awful_bad-minimum_demerits then
> minimum_demerits:=awful_bad-1
> else minimum_demerits:=minimum_demerits+abs(adj_demerits);
> for fit_class:=very_loose_fit to tight_fit do
> begin if minimal_demerits[fit_class]<=minimum_demerits then
> @<Insert a new active node
> from |best_place[fit_class]| to |cur_p|@>;
> minimal_demerits[fit_class]:=awful_bad;
> end;
> minimum_demerits:=awful_bad;
13408c16387
< want to insert `$\delta\,|cur_p|\,\delta^\prime$' before $a$, where
---
> want to insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where
13410c16389
< in the notation explained above. The |cur_active_width| array now hold
---
> in the notation explained above. The |cur_active_width| array now holds
13417c16396
< (which is normally zero) minus the width of nodes following@@|cur_p| that are
---
> (which is normally zero) minus the width of nodes following~|cur_p| that are
13423c16402,16404
< @d set_break_width_to_background(#)==break_width[#]←background[#]
---
> Kern nodes do not disappear at a line break unless they are |explicit|.
>
> @d set_break_width_to_background(#)==break_width[#]:=background[#]
13426,13440c16407,16422
< begin no_break_yet←false; do_all_six(set_break_width_to_background);
< if (break_type=unhyphenated)∨(cur_p=null) then
< begin s←cur_p;
< while s≠null do
< begin if is_char_node(s) then goto done;
< case type(s) of
< glue_node:@<Subtract glue from |break_width|@>;
< penalty_node: do_nothing;
< math_node,kern_node: break_width[1]←break_width[1]-width(s);
< othercases goto done
< endcases;@/
< s←link(s);
< end;
< end
< else @<Compute the discretionary |break_width| values@>;
---
> begin no_break_yet:=false; do_all_six(set_break_width_to_background);
> s:=cur_p;
> if break_type>unhyphenated then if cur_p<>null then
> @<Compute the discretionary |break_width| values@>;
> while s<>null do
> begin if is_char_node(s) then goto done;
> case type(s) of
> glue_node:@<Subtract glue from |break_width|@>;
> penalty_node: do_nothing;
> math_node: break_width[1]:=break_width[1]-width(s);
> kern_node: if subtype(s)<>explicit then goto done
> else break_width[1]:=break_width[1]-width(s);
> othercases goto done
> endcases;@/
> s:=link(s);
> end;
13444,13446c16426,16428
< begin v←glue_ptr(s); break_width[1]←break_width[1]-width(v);
< break_width[2+stretch_order(v)]←break_width[2+stretch_order(v)]-stretch(v);
< break_width[6]←break_width[6]-shrink(v);
---
> begin v:=glue_ptr(s); break_width[1]:=break_width[1]-width(v);
> break_width[2+stretch_order(v)]:=break_width[2+stretch_order(v)]-stretch(v);
> break_width[6]:=break_width[6]-shrink(v);
13451,13452c16433,16434
< Suppose that the pre-break text at |cur_p| has length $l↓0$, the post-break
< text has length $l↓1$, and the replacement text has length $l$. Suppose
---
> Suppose that the pre-break text at |cur_p| has length $l_0$, the post-break
> text has length $l_1$, and the replacement text has length |l|. Suppose
13455,13456c16437,16441
< where $\beta(q)=\beta(|cur_p|)-l↓0+l$. The actual length will be the background
< plus $l↓1$, so the length from |cur_p| to |cur_p| should be $\gamma+l↓0+l↓1-l$.
---
> where $\beta(q)=\beta(|cur_p|)-l_0+l$. The actual length will be the background
> plus $l_1$, so the length from |cur_p| to |cur_p| should be $\gamma+l_0+l_1-l$.
> If the post-break text of the discretionary is empty, a break may also
> discard~|q|; in that unusual case we subtract the length of~|q| and any
> other nodes that will be discarded after the discretionary break.
13458c16443
< The value of $l↓0$ need not be computed, since |line_break| will put
---
> The value of $l_0$ need not be computed, since |line_break| will put
13465c16450
< begin t←replace_count(cur_p); s←cur_p;
---
> begin t:=replace_count(cur_p); v:=cur_p; s:=post_break(cur_p);
13467,13475c16452,16461
< begin decr(t); s←link(s);
< @<Subtract the width of node |s| from |break_width|@>;
< end;
< s←post_break(cur_p);
< while s≠null do
< begin @<Add the width of node |s| to |break_width|@>;
< s←link(s);
< end;
< break_width[1]←break_width[1]+disc_width;
---
> begin decr(t); v:=link(v);
> @<Subtract the width of node |v| from |break_width|@>;
> end;
> while s<>null do
> begin @<Add the width of node |s| to |break_width|@>;
> s:=link(s);
> end;
> break_width[1]:=break_width[1]+disc_width;
> if post_break(cur_p)=null then s:=link(v);
> {nodes may be discardable after the break}
13479c16465
< only character nodes, kern nodes, and ligature nodes.
---
> only character nodes, kern nodes, ligature nodes, and box or rule nodes.
13481,13494c16467,16481
< @<Subtract the width of node |s|...@>=
< if is_char_node(s) then
< begin f←font(s);
< break_width[1]←break_width[1]-char_width(f)(char_info(f)(character(s)));
< end
< else case type(s) of
< ligature_node: begin f←font(lig_char(s));@/
< break_width[1]←@|break_width[1]-
< char_width(f)(char_info(f)(character(lig_char(s))));
< end;
< kern_node: break_width[1]←break_width[1]-width(s);
< othercases confusion("disc1")
< @:confusion disc1}{\quad disc1@>
< endcases
---
> @<Subtract the width of node |v|...@>=
> if is_char_node(v) then
> begin f:=font(v);
> break_width[1]:=break_width[1]-char_width(f)(char_info(f)(character(v)));
> end
> else case type(v) of
> ligature_node: begin f:=font(lig_char(v));@/
> break_width[1]:=@|break_width[1]-
> char_width(f)(char_info(f)(character(lig_char(v))));
> end;
> hlist_node,vlist_node,rule_node,kern_node:
> break_width[1]:=break_width[1]-width(v);
> othercases confusion("disc1")
> @:this can't happen disc1}{\quad disc1@>
> endcases
13498,13509c16485,16497
< begin f←font(s);
< break_width[1]←@|break_width[1]+char_width(f)(char_info(f)(character(s)));
< end
< else case type(s) of
< ligature_node: begin f←font(lig_char(s));
< break_width[1]←break_width[1]+
< char_width(f)(char_info(f)(character(lig_char(s))));
< end;
< kern_node: break_width[1]←break_width[1]+width(s);
< othercases confusion("disc2")
< @:confusion disc2}{\quad disc2@>
< endcases
---
> begin f:=font(s);
> break_width[1]:=@|break_width[1]+char_width(f)(char_info(f)(character(s)));
> end
> else case type(s) of
> ligature_node: begin f:=font(lig_char(s));
> break_width[1]:=break_width[1]+
> char_width(f)(char_info(f)(character(lig_char(s))));
> end;
> hlist_node,vlist_node,rule_node,kern_node:
> break_width[1]:=break_width[1]+width(s);
> othercases confusion("disc2")
> @:this can't happen disc2}{\quad disc2@>
> endcases
13511c16499
< @ We use the fact that |type(active)≠delta_node|.
---
> @ We use the fact that |type(active)<>delta_node|.
13514,13516c16502,16504
< mem[prev_r+#].sc←@|@t\hskip10pt@>mem[prev_r+#].sc
< -cur_active_width[#]+break_width[#]
< @d store_break_width(#)==active_width[#]←break_width[#]
---
> mem[prev_r+#].sc:=@|@t\hskip10pt@>mem[prev_r+#].sc
> -cur_active_width[#]+break_width[#]
> @d store_break_width(#)==active_width[#]:=break_width[#]
13518c16506
< mem[q+#].sc←break_width[#]-cur_active_width[#]
---
> mem[q+#].sc:=break_width[#]-cur_active_width[#]
13522,13523c16510,16511
< begin εo_all_six(convert_to_break_width);
< end
---
> begin do_all_six(convert_to_break_width);
> end
13525,13531c16513,16519
< begin do_all_six(store_break_width);
< end
< else begin q←get_node(delta_node_size); link(q)←r; type(q)←delta_node;
< subtype(q)←0; {the |subtype| is not used}
< do_all_six(new_delta_to_break_width);
< link(prev_r)←q; prev_prev_r←prev_r; prev_r←q;
< end
---
> begin do_all_six(store_break_width);
> end
> else begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
> subtype(q):=0; {the |subtype| is not used}
> do_all_six(new_delta_to_break_width);
> link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
> end
13534c16522
< least one active node before |r|, so |type(prev_r)≠delta_node|.
---
> least one active node before |r|, so |type(prev_r)<>delta_node|.
13536,13537c16524,16525
< @d new_delta_from_break_width(#)==@|mem[q+#].sc←
< cur_active_width[#]-break_width[#]
---
> @d new_delta_from_break_width(#)==@|mem[q+#].sc:=
> cur_active_width[#]-break_width[#]
13540,13545c16528,16533
< if r≠last_active then
< begin q←get_node(delta_node_size); link(q)←r; type(q)←delta_node;
< subtype(q)←0; {the |subtype| is not used}
< do_all_six(new_delta_from_break_width);
< link(prev_r)←q; prev_prev_r←prev_r; prev_r←q;
< end
---
> if r<>last_active then
> begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
> subtype(q):=0; {the |subtype| is not used}
> do_all_six(new_delta_from_break_width);
> link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
> end
13551,13558c16539,16562
< begin q←get_node(passive_node_size);
< link(q)←passive; passive←q; cur_break(q)←cur_p;
< prev_break(q)←best_place[fit_class];@/
< q←get_node(active_node_size); break_node(q)←passive;
< line_number(q)←best_pl_line[fit_class]+1;
< fitness(q)←fit_class; type(q)←break_type;
< total_demerits(q)←minimal_demerits[fit_class];
< link(q)←r; link(prev_r)←q; prev_r←q;
---
> begin q:=get_node(passive_node_size);
> link(q):=passive; passive:=q; cur_break(q):=cur_p;
> @!stat incr(pass_number); serial(q):=pass_number;@+tats@;@/
> prev_break(q):=best_place[fit_class];@/
> q:=get_node(active_node_size); break_node(q):=passive;
> line_number(q):=best_pl_line[fit_class]+1;
> fitness(q):=fit_class; type(q):=break_type;
> total_demerits(q):=minimal_demerits[fit_class];
> link(q):=r; link(prev_r):=q; prev_r:=q;
> @!stat if tracing_paragraphs>0 then
> @<Print a symbolic description of the new break node@>;
> tats@;@/
> end
>
> @ @<Print a symbolic description of the new break node@>=
> begin print_nl("@@@@"); print_int(serial(passive));
> @.\AT!\AT!@>
> print(": line "); print_int(line_number(q)-1);
> print_char("."); print_int(fit_class);
> if break_type=hyphenated then print_char("-");
> print(" t="); print_int(total_demerits(q));
> print(" -> @@@@");
> if prev_break(passive)=null then print_char("0")
> else print_int(serial(prev_break(passive)));
13568,13574c16572,16578
< |n=hang_after|; if |n≥0|, hanging indentation takes place on lines |n+1|,
< |n+2|, $\ldotss$, otherwise it takes place on lines 1, $\ldotss$, $\leftv
< n\rightv$. When hanging indentation is active, the left margin is
< |hanging_indent|, if |hanging_indent≥0|, else it is 0; the line length is
< $|hsize|-\leftv|hanging_indent|\rightv$. The normal setting is
< |par_shape_ptr=null|, |hang_after=0|, and |hanging_indent=0|.
< Note that if |hanging_indent=0|, the value of |hang_after| is irrelevant.
---
> |n=hang_after|; if |n>=0|, hanging indentation takes place on lines |n+1|,
> |n+2|, \dots, otherwise it takes place on lines 1, \dots, $\vert
> n\vert$. When hanging indentation is active, the left margin is
> |hang_indent|, if |hang_indent>=0|, else it is 0; the line length is
> $|hsize|-\vert|hang_indent|\vert$. The normal setting is
> |par_shape_ptr=null|, |hang_after=1|, and |hang_indent=0|.
> Note that if |hang_indent=0|, the value of |hang_after| is irrelevant.
13580,13582c16584,16586
< the same width}
< @!first_width:scaled; {the width of all lines |≤last_special_line|, if
< no \.{\\parshape} has been specified}
---
> the same width}
> @!first_width:scaled; {the width of all lines |<=last_special_line|, if
> no \.{\\parshape} has been specified}
13590c16594
< @<Get ready...@>=
---
> @<Get ready to start...@>=
13592,13602c16596,16606
< if hanging_indent=0 then
< begin last_special_line←0; first_width←hsize;
< second_width←first_width;
< end
< else @<Set line length parameters in preparation for hanging indentation@>
< else begin last_special_line←info(par_shape_ptr)-1;
< second_width←mem[par_shape_ptr+2*(last_special_line+1)].sc;
< second_indent←mem[par_shape_ptr+2*last_special_line+1].sc;
< end;
< if looseness=0 then easy_line←last_special_line
< else easy_line←max_halfword
---
> if hang_indent=0 then
> begin last_special_line:=0; second_width:=hsize;
> second_indent:=0;
> end
> else @<Set line length parameters in preparation for hanging indentation@>
> else begin last_special_line:=info(par_shape_ptr)-1;
> second_width:=mem[par_shape_ptr+2*(last_special_line+1)].sc;
> second_indent:=mem[par_shape_ptr+2*last_special_line+1].sc;
> end;
> if looseness=0 then easy_line:=last_special_line
> else easy_line:=max_halfword
13605c16609
< begin last_special_line←abs(hang_after);
---
> begin last_special_line:=abs(hang_after);
13607,13616c16611,16620
< begin first_width←hsize-abs(hanging_indent);
< if hanging_indent≥0 then first_indent←hanging_indent
< else first_indent←0;
< second_width←hsize; second_indent←0;
< end
< else begin first_width←hsize; first_indent←0;
< second_width←hsize-abs(hanging_indent);
< if hanging_indent≥0 then second_indent←hanging_indent
< else second_indent←0;
< end;
---
> begin first_width:=hsize-abs(hang_indent);
> if hang_indent>=0 then first_indent:=hang_indent
> else first_indent:=0;
> second_width:=hsize; second_indent:=0;
> end
> else begin first_width:=hsize; first_indent:=0;
> second_width:=hsize-abs(hang_indent);
> if hang_indent>=0 then second_indent:=hang_indent
> else second_indent:=0;
> end;
13620,13621c16624,16625
< active node@@|r| whose |line_number| field contains |l|. Thus we want to
< compute the length of the $l\,$th line of the current paragraph. Furthermore
---
> active node~|r| whose |line_number| field contains |l|. Thus we want to
> compute the length of the $l\mskip1mu$th line of the current paragraph. Furthermore,
13623c16627
< equivalent to@@|l|.
---
> equivalent to~|l|.
13627,13633c16631,16637
< begin line_width←second_width; old_l←max_halfword-1;
< end
< else begin old_l←l;
< if l>last_special_line then line_width←second_width
< else if par_shape_ptr=null then line_width←first_width
< else line_width←mem[par_shape_ptr+2*l@,].sc;
< end
---
> begin line_width:=second_width; old_l:=max_halfword-1;
> end
> else begin old_l:=l;
> if l>last_special_line then line_width:=second_width
> else if par_shape_ptr=null then line_width:=first_width
> else line_width:=mem[par_shape_ptr+2*l@,].sc;
> end
13642c16646
< We also deactivate node@@|r| when a break at@@|cur_p| is forced, since future
---
> We also deactivate node~|r| when a break at~|cur_p| is forced, since future
13646,13648c16650,16655
< begin if cur_active_width[1]<line_width then
< @<Set the value of |b| to the badness for stretching the line,
< and compute the corresponding |fit_class|@>
---
> begin artificial_demerits:=false;@/
> @^inner loop@>
> shortfall:=line_width-cur_active_width[1]; {we're this much too short}
> if shortfall>0 then
> @<Set the value of |b| to the badness for stretching the line,
> and compute the corresponding |fit_class|@>
13650,13657c16657,16664
< and compute the corresponding |fit_class|@>;
< if (b>inf_bad)∨(pi=eject_penalty) then
< @<Prepare to deactivate node@@|r|, and |goto deactivate| unless
< there is a reason to consider lines of text from |r| to |cur_p|@>
< else begin prev_r←r;
< if b>threshold then goto continue;
< node_r_stays_active←true;
< end;
---
> and compute the corresponding |fit_class|@>;
> if (b>inf_bad)or(pi=eject_penalty) then
> @<Prepare to deactivate node~|r|, and |goto deactivate| unless
> there is a reason to consider lines of text from |r| to |cur_p|@>
> else begin prev_r:=r;
> if b>threshold then goto continue;
> node_r_stays_active:=true;
> end;
13663c16670
< @ When a line must stretch, the available stretchability appears in the
---
> @ When a line must stretch, the available stretchability can be found in the
13665a16673,16678
> The present section is part of \TeX's inner loop, and it is most often performed
> when the badness is infinite; therefore it is worth while to make a quick
> test for large width excess and small stretchability, before calling the
> |badness| subroutine.
> @^inner loop@>
>
13667,13676c16680,16693
< if (cur_active_width[3]≠0)∨(cur_active_width[4]≠0)∨@|
< (cur_active_width[5]≠0) then
< begin b←0; fit_class←decent_fit; {infinite stretch}
< end
< else begin b←badness(line_width-cur_active_width[1],cur_active_width[2]);
< if b>12 then
< if b>99 then fit_class←very_loose_fit
< else fit_class←loose_fit
< else fit_class←decent_fit;
< end
---
> if (cur_active_width[3]<>0)or(cur_active_width[4]<>0)or@|
> (cur_active_width[5]<>0) then
> begin b:=0; fit_class:=decent_fit; {infinite stretch}
> end
> else begin if shortfall>7230584 then if cur_active_width[2]<1663497 then
> begin b:=inf_bad; fit_class:=very_loose_fit; goto done1;
> end;
> b:=badness(shortfall,cur_active_width[2]);
> if b>12 then
> if b>99 then fit_class:=very_loose_fit
> else fit_class:=loose_fit
> else fit_class:=decent_fit;
> done1:
> end
13682,13685c16699,16701
< begin if cur_active_width[1]-line_width>cur_active_width[6] then
< b←inf_bad+1
< else b←badness(cur_active_width[1]-line_width,cur_active_width[6]);
< if b>12 then fit_class←tight_fit@+else fit_class←decent_fit;
---
> begin if -shortfall>cur_active_width[6] then b:=inf_bad+1
> else b:=badness(-shortfall,cur_active_width[6]);
> if b>12 then fit_class:=tight_fit@+else fit_class:=decent_fit;
13688c16704
< @ During the second pass, we dare not lose all active nodes, lest we lose
---
> @ During the final pass, we dare not lose all active nodes, lest we lose
13697,13700c16713,16717
< @<Prepare to deactivate node@@|r|, and |goto deactivate| unless...@>=
< begin if second_pass ∧ (minimum_demerits=awful_bad) ∧@|
< (link(r)=last_active) ∧
< (prev_r=active) then b←0 {set badness zero, this break is forced}
---
> @<Prepare to deactivate node~|r|, and |goto deactivate| unless...@>=
> begin if final_pass and (minimum_demerits=awful_bad) and@|
> (link(r)=last_active) and
> (prev_r=active) then
> artificial_demerits:=true {set demerits zero, this break is forced}
13702c16719
< node_r_stays_active←false;
---
> node_r_stays_active:=false;
13706c16723
< feasible, its badness is@@|b|, and its fitness classification is |fit_class|.
---
> feasible, its badness is~|b|, and its fitness classification is |fit_class|.
13713,13732c16730,16799
< @<Compute the fewest total demerits, |d|,
< from the beginning to |cur_p| via@@|r|@>;
< if d≤minimal_demerits[fit_class] then
< begin minimal_demerits[fit_class]←d;
< best_place[fit_class]←break_node(r); best_pl_line[fit_class]←l;
< if d<minimum_demerits then minimum_demerits←d;
< end
<
< @ @<Compute the fewest total demerits...@>=
< if pi≥0 then
< begin d←(line_penalty+b+pi); d←d*d;
< end
< else begin d←line_penalty+b; d←d*d;
< if pi>eject_penalty then d←d-pi*pi;
< end;
< d←d+total_demerits(r);
< if (break_type=hyphenated)∧(type(r)=hyphenated) then
< if cur_p≠null then d←d+double_hyphen_demerits
< else d←d+final_hyphen_demerits;
< if abs(fit_class-fitness(r))>1 then d←d+adj_demerits
---
> if artificial_demerits then d:=0
> else @<Compute the demerits, |d|, from |r| to |cur_p|@>;
> @!stat if tracing_paragraphs>0 then
> @<Print a symbolic description of this feasible break@>;
> tats@;@/
> d:=d+total_demerits(r); {this is the minimum total demerits
> from the beginning to |cur_p| via |r|}
> if d<=minimal_demerits[fit_class] then
> begin minimal_demerits[fit_class]:=d;
> best_place[fit_class]:=break_node(r); best_pl_line[fit_class]:=l;
> if d<minimum_demerits then minimum_demerits:=d;
> end
>
> @ @<Print a symbolic description of this feasible break@>=
> begin if printed_node<>cur_p then
> @<Print the list between |printed_node| and |cur_p|,
> then set |printed_node:=cur_p|@>;
> print_nl("@@");
> @.\AT!@>
> if cur_p=null then print_esc("par")
> else if type(cur_p)<>glue_node then
> begin if type(cur_p)=penalty_node then print_esc("penalty")
> else if type(cur_p)=disc_node then print_esc("discretionary")
> else if type(cur_p)=kern_node then print_esc("kern")
> else print_esc("math");
> end;
> print(" via @@@@");
> if break_node(r)=null then print_char("0")
> else print_int(serial(break_node(r)));
> print(" b=");
> if b>inf_bad then print_char("*")@+else print_int(b);
> @.*\relax@>
> print(" p="); print_int(pi); print(" d=");
> if artificial_demerits then print_char("*")@+else print_int(d);
> end
>
> @ @<Print the list between |printed_node| and |cur_p|...@>=
> begin print_nl("");
> if cur_p=null then short_display(link(printed_node))
> else begin save_link:=link(cur_p);
> link(cur_p):=null; print_nl(""); short_display(link(printed_node));
> link(cur_p):=save_link;
> end;
> printed_node:=cur_p;
> end
>
> @ When the data for a discretionary break is being displayed, we will have
> printed the |pre_break| and |post_break| lists; we want to skip over the
> third list, so that the discretionary data will not appear twice. The
> following code is performed at the very end of |try_break|.
>
> @<Update the value of |printed_node|...@>=
> if cur_p=printed_node then if cur_p<>null then if type(cur_p)=disc_node then
> begin t:=replace_count(cur_p);
> while t>0 do
> begin decr(t); printed_node:=link(printed_node);
> end;
> end
>
> @ @<Compute the demerits, |d|, from |r| to |cur_p|@>=
> begin d:=line_penalty+b;
> if abs(d)>=10000 then d:=100000000@+else d:=d*d;
> if pi<>0 then
> if pi>0 then d:=d+pi*pi
> else if pi>eject_penalty then d:=d-pi*pi;
> if (break_type=hyphenated)and(type(r)=hyphenated) then
> if cur_p<>null then d:=d+double_hyphen_demerits
> else d:=d+final_hyphen_demerits;
> if abs(fit_class-fitness(r))>1 then d:=d+adj_demerits;
> end
13738c16805
< to@@|cur_p|.
---
> to~|cur_p|.
13740,13742c16807,16809
< @d combine_two_deltas(#)==@|mem[prev_r+#].sc←mem[prev_r+#].sc+mem[r+#].sc
< @d downdate_width(#)==@|cur_active_width[#]←cur_active_width[#]-
< mem[prev_r+#].sc
---
> @d combine_two_deltas(#)==@|mem[prev_r+#].sc:=mem[prev_r+#].sc+mem[r+#].sc
> @d downdate_width(#)==@|cur_active_width[#]:=cur_active_width[#]-
> mem[prev_r+#].sc
13745c16812
< link(prev_r)←link(r); free_node(r,active_node_size);
---
> link(prev_r):=link(r); free_node(r,active_node_size);
13747c16814
< node has been deleted@>
---
> node has been deleted@>
13749,13760c16816,16827
< begin r←link(prev_r);
< if r=last_active then
< begin do_all_six(downdate_width);
< link(prev_prev_r)←last_active;
< free_node(prev_r,delta_node_size); prev_r←prev_prev_r;
< end
< else if type(r)=delta_node then
< begin do_all_six(update_width);
< do_all_six(combine_two_deltas);
< link(prev_r)←link(r); free_node(r,delta_node_size);
< end;
< end
---
> begin r:=link(prev_r);
> if r=last_active then
> begin do_all_six(downdate_width);
> link(prev_prev_r):=last_active;
> free_node(prev_r,delta_node_size); prev_r:=prev_prev_r;
> end
> else if type(r)=delta_node then
> begin do_all_six(update_width);
> do_all_six(combine_two_deltas);
> link(prev_r):=link(r); free_node(r,delta_node_size);
> end;
> end
13762c16829
< @ The following code uses the fact that |type(last_active)≠delta_node|. If the
---
> @ The following code uses the fact that |type(last_active)<>delta_node|. If the
13767c16834
< @d update_active(#)==active_width[#]←active_width[#]+mem[r+#].sc
---
> @d update_active(#)==active_width[#]:=active_width[#]+mem[r+#].sc
13770c16837
< begin r←link(active);
---
> begin r:=link(active);
13772,13775c16839,16842
< begin do_all_six(update_active);
< do_all_six(copy_to_cur_active);
< link(active)←link(r); free_node(r,delta_node_size);
< end;
---
> begin do_all_six(update_active);
> do_all_six(copy_to_cur_active);
> link(active):=link(r); free_node(r,delta_node_size);
> end;
13777c16844,16845
< @* \[38] Breaking paragraphs into lines, continued.
---
>
> @* \[39] Breaking paragraphs into lines, continued.
13782c16850
< The main loop of |line_break| traverses the hlist of the given paragraph,
---
> The main loop of |line_break| traverses the given hlist,
13789c16857
< local variable, |prev_p|, is usually one step behind |cur_p|, but the real
---
> variable, |prev_p|, is usually one step behind |cur_p|, but the real
13792c16860
< point to a glue node, penalty node, kern node, or math node.
---
> point to a glue node, penalty node, explicit kern node, or math node.
13800c16868
< @!q,@!r,@!s:pointer; {miscellaneous nodes of temporary interest}
---
> @!q,@!r,@!s,@!prev_s:pointer; {miscellaneous nodes of temporary interest}
13803,13804c16871,16872
< @ The `\!|loop|\unskip' in the following code is performed at most
< twice per call of |line_break|, since it is actually a pass over the
---
> @ The `\ignorespaces|loop|\unskip' in the following code is performed at most
> thrice per call of |line_break|, since it is actually a pass over the
13808,13826c16876,16912
< threshold←pretolerance; second_pass←false;
< loop@+ begin @<Create an active breakpoint representing the beginning of
< the paragraph@>;
< cur_p←link(temp_head); auto_breaking←true;@/
< prev_p←cur_p; {glue at beginning is not a legal breakpoint}
< while (cur_p≠null)∧(link(active)≠last_active) do
< @<Call |try_break| if |cur_p| is a legal breakpoint;
< on the second pass, also try to hyphenate the next
< word, if |cur_p| is a glue node;
< then advance |cur_p| to the next node of the paragraph
< that could possibly be a legal breakpoint@>;
< if cur_p=null then
< @<Try the final line break at the end of the paragraph,
< and |goto done| if the desired breakpoints have been found@>;
< @<Clean up the memory by removing the break nodes@>;
< threshold←tolerance; second_pass←true; {if at first you don't
< succeed, $\ldotss$}
< end;
< done:
---
> threshold:=pretolerance;
> if threshold>=0 then
> begin @!stat if tracing_paragraphs>0 then
> begin begin_diagnostic; print_nl("@@firstpass");@+end;@;@+tats@;@/
> second_pass:=false; final_pass:=false;
> end
> else begin threshold:=tolerance; second_pass:=true;
> final_pass:=(emergency_stretch<=0);
> @!stat if tracing_paragraphs>0 then begin_diagnostic;@+tats@;
> end;
> loop@+ begin if threshold>inf_bad then threshold:=inf_bad;
> if second_pass then @<Initialize for hyphenating a paragraph@>;
> @<Create an active breakpoint representing the beginning of the paragraph@>;
> cur_p:=link(temp_head); auto_breaking:=true;@/
> prev_p:=cur_p; {glue at beginning is not a legal breakpoint}
> while (cur_p<>null)and(link(active)<>last_active) do
> @<Call |try_break| if |cur_p| is a legal breakpoint;
> on the second pass, also try to hyphenate the next
> word, if |cur_p| is a glue node;
> then advance |cur_p| to the next node of the paragraph
> that could possibly be a legal breakpoint@>;
> if cur_p=null then
> @<Try the final line break at the end of the paragraph,
> and |goto done| if the desired breakpoints have been found@>;
> @<Clean up the memory by removing the break nodes@>;
> if not second_pass then
> begin@!stat if tracing_paragraphs>0 then print_nl("@@secondpass");@;@+tats@/
> threshold:=tolerance; second_pass:=true; final_pass:=(emergency_stretch<=0);
> end {if at first you don't succeed, \dots}
> else begin @!stat if tracing_paragraphs>0 then
> print_nl("@@emergencypass");@;@+tats@/
> background[2]:=background[2]+emergency_stretch; final_pass:=true;
> end;
> end;
> done: @!stat if tracing_paragraphs>0 then
> begin end_diagnostic(true); normalize_selector;
> end;@+tats@/
13829c16915
< corresponding passive node.
---
> corresponding passive node.
13831c16917
< @d store_background(#)==active_width[#]←background[#]
---
> @d store_background(#)==active_width[#]:=background[#]
13834,13837c16920,16923
< q←get_node(active_node_size);
< type(q)←unhyphenated; fitness(q)←decent_fit;
< link(q)←last_active; break_node(q)←null;
< line_number(q)←already+1; total_demerits(q)←0; link(active)←q;
---
> q:=get_node(active_node_size);
> type(q):=unhyphenated; fitness(q):=decent_fit;
> link(q):=last_active; break_node(q):=null;
> line_number(q):=prev_graf+1; total_demerits(q):=0; link(active):=q;
13839c16925,16926
< passive←null
---
> passive:=null; printed_node:=temp_head; pass_number:=0;
> font_in_short_display:=null_font
13842,13854c16929,16941
< q←link(active);
< while q≠last_active do
< begin cur_p←link(q);
< if type(q)=delta_node then free_node(q,delta_node_size)
< else free_node(q,active_node_size);
< q←cur_p;
< end;
< q←passive;
< while q≠null do
< begin cur_p←link(q);
< free_node(q,passive_node_size);
< q←cur_p;
< end
---
> q:=link(active);
> while q<>last_active do
> begin cur_p:=link(q);
> if type(q)=delta_node then free_node(q,delta_node_size)
> else free_node(q,active_node_size);
> q:=cur_p;
> end;
> q:=passive;
> while q<>null do
> begin cur_p:=link(q);
> free_node(q,passive_node_size);
> q:=cur_p;
> end
13863,13867c16950,16953
< @d kern_break==begin@t@>@;@/
< if not is_char_node(link(cur_p)) ∧ auto_breaking then
< if type(link(cur_p))=glue_node then try_break(0,unhyphenated);
< act_width←act_width+width(cur_p);
< end
---
> @d kern_break==begin if not is_char_node(link(cur_p)) and auto_breaking then
> if type(link(cur_p))=glue_node then try_break(0,unhyphenated);
> act_width:=act_width+width(cur_p);
> end
13870,13871c16956,16958
< begin while is_char_node(cur_p) do
< @<Advance \(c)|cur_p| to the node following the present character@>;
---
> begin if is_char_node(cur_p) then
> @<Advance \(c)|cur_p| to the node following the present
> string of characters@>;
13873,13886c16960,16974
< hlist_node,vlist_node,rule_node: act_width←act_width+width(cur_p);
< whatsit_node: @<Advance \(p)past a whatsit node in the |line_break| loop@>;
< glue_node: begin @<If node |cur_p| is a legal breakpoint, call |try_break|@>;
< @<Update the active widths by including the glue in |glue_ptr(cur_p)|@>;
< if second_pass ∧ auto_breaking then
< @<Try to hyphenate the following word@>;
< end;
< kern_node: kern_break;
< ligature_node: begin f←font(lig_char(cur_p));
< act_width←act_width+char_width(f)(char_info(f)(character(lig_char(cur_p))));
< end;
< disc_node: @<Try to break after a discretionary fragment@>;
< math_node: begin auto_breaking←(subtype(cur_p)=after); kern_break;
< end;
---
> hlist_node,vlist_node,rule_node: act_width:=act_width+width(cur_p);
> whatsit_node: @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>;
> glue_node: begin @<If node |cur_p| is a legal breakpoint, call |try_break|;
> then update the active widths by including the glue in |glue_ptr(cur_p)|@>;
> if second_pass and auto_breaking then
> @<Try to hyphenate the following word@>;
> end;
> kern_node: if subtype(cur_p)=explicit then kern_break
> else act_width:=act_width+width(cur_p);
> ligature_node: begin f:=font(lig_char(cur_p));
> act_width:=act_width+char_width(f)(char_info(f)(character(lig_char(cur_p))));
> end;
> disc_node: @<Try to break after a discretionary fragment, then |goto done5|@>;
> math_node: begin auto_breaking:=(subtype(cur_p)=after); kern_break;
> end;
13890c16978
< @:confusion paragraph}{\quad paragraph@>
---
> @:this can't happen paragraph}{\quad paragraph@>
13892,13893c16980,16981
< prev_p←cur_p; cur_p←link(cur_p);
< end
---
> prev_p:=cur_p; cur_p:=link(cur_p);
> done5:end
13899a16988
> @^inner loop@>
13901,13904c16990,16995
< @<Advance \(c)|cur_p| to the node following the present character@>=
< begin f←font(cur_p);
< act_width←act_width+char_width(f)(char_info(f)(character(cur_p)));
< cur_p←link(cur_p);
---
> @<Advance \(c)|cur_p| to the node following the present string...@>=
> begin prev_p:=cur_p;
> repeat f:=font(cur_p);
> act_width:=act_width+char_width(f)(char_info(f)(character(cur_p)));
> cur_p:=link(cur_p);
> until not is_char_node(cur_p);
13912,13922c17003,17012
< begin if is_char_node(prev_p) then try_break(0,unhyphenated)
< else if precedes_break(prev_p) then try_break(0,unhyphenated);
< end
<
< @ @<Update the active widths by including the glue in |glue_ptr(cur_p)|@>=
< begin check_shrinkage(glue_ptr(cur_p)); q←glue_ptr(cur_p);
< act_width←act_width+width(q);@|
< active_width[2+stretch_order(q)]←@|
< active_width[2+stretch_order(q)]+stretch(q);@/
< active_width[6]←active_width[6]+shrink(q);
< end
---
> begin if is_char_node(prev_p) then try_break(0,unhyphenated)
> else if precedes_break(prev_p) then try_break(0,unhyphenated)
> else if (type(prev_p)=kern_node)and(subtype(prev_p)<>explicit) then
> try_break(0,unhyphenated);
> end;
> check_shrinkage(glue_ptr(cur_p)); q:=glue_ptr(cur_p);
> act_width:=act_width+width(q);@|
> active_width[2+stretch_order(q)]:=@|
> active_width[2+stretch_order(q)]+stretch(q);@/
> active_width[6]:=active_width[6]+shrink(q)
13925c17015
< only character nodes, kern nodes, and ligature nodes.
---
> only character nodes, kern nodes, box nodes, rule nodes, and ligature nodes.
13927,13928c17017,17018
< @<Try to break after a discretionary fragment@>=
< begin s←pre_break(cur_p);
---
> @<Try to break after a discretionary fragment...@>=
> begin s:=pre_break(cur_p); disc_width:=0;
13930,13937c17020,17032
< else begin disc_width←0;
< repeat @<Add the width of node |s| to |disc_width|@>;
< s←link(s);
< until s=null;
< act_width←act_width+disc_width;
< try_break(hyphen_penalty,hyphenated);
< act_width←act_width-disc_width;
< end;
---
> else begin repeat @<Add the width of node |s| to |disc_width|@>;
> s:=link(s);
> until s=null;
> act_width:=act_width+disc_width;
> try_break(hyphen_penalty,hyphenated);
> act_width:=act_width-disc_width;
> end;
> r:=replace_count(cur_p); s:=link(cur_p);
> while r>0 do
> begin @<Add the width of node |s| to |act_width|@>;
> decr(r); s:=link(s);
> end;
> prev_p:=cur_p; cur_p:=s; goto done5;
13942,13953c17037,17065
< begin f←font(s);
< disc_width←disc_width+char_width(f)(char_info(f)(character(s)));
< end
< else case type(s) of
< ligature_node: begin f←font(lig_char(s));
< disc_width←disc_width+
< char_width(f)(char_info(f)(character(lig_char(s))));
< end;
< kern_node: disc_width←disc_width+width(s);
< othercases confusion("disc3")
< @:confusion disc3}{\quad disc3@>
< endcases
---
> begin f:=font(s);
> disc_width:=disc_width+char_width(f)(char_info(f)(character(s)));
> end
> else case type(s) of
> ligature_node: begin f:=font(lig_char(s));
> disc_width:=disc_width+
> char_width(f)(char_info(f)(character(lig_char(s))));
> end;
> hlist_node,vlist_node,rule_node,kern_node:
> disc_width:=disc_width+width(s);
> othercases confusion("disc3")
> @:this can't happen disc3}{\quad disc3@>
> endcases
>
> @ @<Add the width of node |s| to |act_width|@>=
> if is_char_node(s) then
> begin f:=font(s);
> act_width:=act_width+char_width(f)(char_info(f)(character(s)));
> end
> else case type(s) of
> ligature_node: begin f:=font(lig_char(s));
> act_width:=act_width+
> char_width(f)(char_info(f)(character(lig_char(s))));
> end;
> hlist_node,vlist_node,rule_node,kern_node:
> act_width:=act_width+width(s);
> othercases confusion("disc4")
> @:this can't happen disc4}{\quad disc4@>
> endcases
13958c17070
< correct ``looseness.'' On the second pass, there will be at least one active
---
> correct ``looseness.'' On the final pass, there will be at least one active
13970c17082
< and the optimum |best_line|}
---
> and the optimum |best_line|}
13972c17084
< the optimum |best_line|}
---
> the optimum |best_line|}
13976,13981c17088,17093
< if link(active)≠last_active then
< begin @<Find an active node with fewest demerits@>;
< if looseness=0 then goto done;
< @<Find the best active node for the desired looseness@>;
< if (actual_looseness=looseness)∨ second_pass then goto done;
< end;
---
> if link(active)<>last_active then
> begin @<Find an active node with fewest demerits@>;
> if looseness=0 then goto done;
> @<Find the best active node for the desired looseness@>;
> if (actual_looseness=looseness)or final_pass then goto done;
> end;
13985,13989c17097,17101
< r←link(active); fewest_demerits←awful_bad;
< repeat if type(r)≠delta_node then if total_demerits(r)<fewest_demerits then
< begin fewest_demerits←total_demerits(r); best_bet←r;
< end;
< r←link(r);
---
> r:=link(active); fewest_demerits:=awful_bad;
> repeat if type(r)<>delta_node then if total_demerits(r)<fewest_demerits then
> begin fewest_demerits:=total_demerits(r); best_bet:=r;
> end;
> r:=link(r);
13991c17103
< best_line←line_number(best_bet)
---
> best_line:=line_number(best_bet)
13999,14012c17111,17124
< begin r←link(active); actual_looseness←0;
< repeat if type(r)≠delta_node then
< begin line_diff←line_number(cur_p)-best_line;
< if ((line_diff<actual_looseness)∧(looseness≤line_diff))∨@|
< ((line_diff>actual_looseness)∧(looseness≥line_diff)) then
< begin best_bet←cur_p; actual_looseness←line_diff;
< fewest_demerits←total_demerits(r);
< end
< else if (line_diff=actual_looseness)∧@|
< (total_demerits(r)<fewest_demerits) then
< begin best_bet←cur_p; fewest_demerits←total_demerits(r);
< end;
< end;
< r←link(r);
---
> begin r:=link(active); actual_looseness:=0;
> repeat if type(r)<>delta_node then
> begin line_diff:=line_number(r)-best_line;
> if ((line_diff<actual_looseness)and(looseness<=line_diff))or@|
> ((line_diff>actual_looseness)and(looseness>=line_diff)) then
> begin best_bet:=r; actual_looseness:=line_diff;
> fewest_demerits:=total_demerits(r);
> end
> else if (line_diff=actual_looseness)and@|
> (total_demerits(r)<fewest_demerits) then
> begin best_bet:=r; fewest_demerits:=total_demerits(r);
> end;
> end;
> r:=link(r);
14014c17126
< best_line←line_number(best_bet);
---
> best_line:=line_number(best_bet);
14017c17129
< @ Once the best sequence of breakpoints has been found, we call on the
---
> @ Once the best sequence of breakpoints has been found (hurray), we call on the
14026c17138
< is |best_line-already-1|. The last breakpoint is specified by
---
> is |best_line-prev_graf-1|. The last breakpoint is specified by
14040a17153
> @!post_disc_break:boolean; {and did it have a nonempty post-break part?}
14047,14048c17160,17161
< first breakpoint@>;
< cur_line←already+1;
---
> first breakpoint@>;
> cur_line:=prev_graf+1;
14050,14053c17163,17167
< current vertical list, together with associated penalties and other
< insertions@>;
< incr(cur_line); cur_p←next_break(cur_p);
< if cur_p≠null then @<Prune unwanted nodes at the beginning of the next line@>;
---
> current vertical list, together with associated penalties and other
> insertions@>;
> incr(cur_line); cur_p:=next_break(cur_p);
> if cur_p<>null then if not post_disc_break then
> @<Prune unwanted nodes at the beginning of the next line@>;
14055,14058c17169,17172
< if (cur_line≠best_line)∨(link(temp_head)≠null) then
< confusion("line breaking");
< @:confusion line breaking}{\quad line breaking@>
< already←best_line-1;
---
> if (cur_line<>best_line)or(link(temp_head)<>null) then
> confusion("line breaking");
> @:this can't happen line breaking}{\quad line breaking@>
> prev_graf:=best_line-1;
14068,14069c17182,17183
< q←break_node(best_bet); cur_p←null;
< repeat r←q; q←prev_break(q); next_break(r)←cur_p; cur_p←r;
---
> q:=break_node(best_bet); cur_p:=null;
> repeat r:=q; q:=prev_break(q); next_break(r):=cur_p; cur_p:=r;
14073,14074c17187,17189
< a line, except in the unusual case that the node to be deleted is actually
< one of the chosen breakpoints. The pruning done here is designed to match
---
> a line, except in the anomalous case that the node to be deleted is actually
> one of the chosen breakpoints. Otherwise
> the pruning done here is designed to match
14079,14091c17194,17207
< begin r←temp_head;
< loop@+ begin q←link(r);
< if q=cur_break(cur_p) then goto done1;
< {|cur_break(cur_p)| is the next breakpoint}
< {now |q| cannot be |null|}
< if is_char_node(q) then goto done1;
< if non_discardable(q) then goto done1;
< r←q; {now |type(q)=glue_node|, |kern_node|, |math_node| or |penalty_node|}
< end;
< done1: if r≠temp_head then
< begin link(r)←null; flush_node_list(link(temp_head));
< link(temp_head)←q;
< end;
---
> begin r:=temp_head;
> loop@+ begin q:=link(r);
> if q=cur_break(cur_p) then goto done1;
> {|cur_break(cur_p)| is the next breakpoint}
> {now |q| cannot be |null|}
> if is_char_node(q) then goto done1;
> if non_discardable(q) then goto done1;
> if type(q)=kern_node then if subtype(q)<>explicit then goto done1;
> r:=q; {now |type(q)=glue_node|, |kern_node|, |math_node| or |penalty_node|}
> end;
> done1: if r<>temp_head then
> begin link(r):=null; flush_node_list(link(temp_head));
> link(temp_head):=q;
> end;
14104c17220
< \.{\\rightskip}; also set the proper value of |disc_break|@>;
---
> \.{\\rightskip}; also set the proper value of |disc_break|@>;
14108c17224
< special nodes taken out of the box by the packager@>;
---
> special nodes taken out of the box by the packager@>;
14115,14130c17231,17246
< q←cur_break(cur_p); disc_break←false;
< if q≠null then {|q| cannot be a |char_node|}
< if type(q)=glue_node then
< begin delete_glue_ref(glue_ptr(q));
< glue_ptr(q)←right_skip;
< subtype(q)←right_skip_code+1; add_glue_ref(right_skip);
< goto done;
< end
< else begin if type(q)=disc_node then
< @<Change discretionary to compulsory and set
< |disc_break←true|@>;
< if (type(q)=math_node)∨(type(q)=kern_node) then width(q)←0;
< end
< else begin q←temp_head;
< while link(q)≠null do q←link(q);
< end;
---
> q:=cur_break(cur_p); disc_break:=false; post_disc_break:=false;
> if q<>null then {|q| cannot be a |char_node|}
> if type(q)=glue_node then
> begin delete_glue_ref(glue_ptr(q));
> glue_ptr(q):=right_skip;
> subtype(q):=right_skip_code+1; add_glue_ref(right_skip);
> goto done;
> end
> else begin if type(q)=disc_node then
> @<Change discretionary to compulsory and set
> |disc_break:=true|@>
> else if (type(q)=math_node)or(type(q)=kern_node) then width(q):=0;
> end
> else begin q:=temp_head;
> while link(q)<>null do q:=link(q);
> end;
14135,14157c17251,17268
< begin t←replace_count(q);
< @<Destroy the |t| nodes following |q|, but save the last one if it is
< a necessary kern; make |r| point to the following node@>;
< if post_break(q)≠null then @<Transplant the post-break list@>;
< if pre_break(q)≠null then @<Transplant the pre-break list@>;
< link(q)←r; disc_break←true;
< end
<
< @ A subtle bug that would perhaps never have been detected is avoided here
< by preserving a kern node that just might equal |cur_break(next_break(cur_p))|.
<
< @<Destroy the |t| nodes following |q|...@>=
< if t=0 then r←link(q)
< else begin r←q;
< while t>1 do
< begin r←link(r); decr(t);
< end;
< s←link(r);
< if ¬is_char_node(s) then if next_break(cur_p)≠null then
< if cur_break(next_break(cur_p))=s then s←r;
< r←link(s); link(s)←null;
< flush_node_list(link(q)); replace_count(q)←0;
< end
---
> begin t:=replace_count(q);
> @<Destroy the |t| nodes following |q|, and
> make |r| point to the following node@>;
> if post_break(q)<>null then @<Transplant the post-break list@>;
> if pre_break(q)<>null then @<Transplant the pre-break list@>;
> link(q):=r; disc_break:=true;
> end
>
> @ @<Destroy the |t| nodes following |q|...@>=
> if t=0 then r:=link(q)
> else begin r:=q;
> while t>1 do
> begin r:=link(r); decr(t);
> end;
> s:=link(r);
> r:=link(s); link(s):=null;
> flush_node_list(link(q)); replace_count(q):=0;
> end
14163,14165c17274,17276
< begin s←post_break(q);
< while link(s)≠null do s←link(s);
< link(s)←r; r←post_break(q); post_break(q)←null;
---
> begin s:=post_break(q);
> while link(s)<>null do s:=link(s);
> link(s):=r; r:=post_break(q); post_break(q):=null; post_disc_break:=true;
14172,14174c17283,17285
< begin s←pre_break(q); link(q)←s;
< while link(s)≠null do s←link(s);
< pre_break(q)←null; q←s;
---
> begin s:=pre_break(q); link(q):=s;
> while link(s)<>null do s:=link(s);
> pre_break(q):=null; q:=s;
14178c17289
< r←new_param_glue(right_skip_code); link(r)←link(q); link(q)←r; q←r
---
> r:=new_param_glue(right_skip_code); link(r):=link(q); link(q):=r; q:=r
14185,14189c17296,17307
< r←link(q); link(q)←null; q←link(temp_head); link(temp_head)←r;
< if left_skip≠zero_glue then
< begin r←new_param_glue(left_skip_code);
< link(r)←q; q←r;
< end
---
> r:=link(q); link(q):=null; q:=link(temp_head); link(temp_head):=r;
> if left_skip<>zero_glue then
> begin r:=new_param_glue(left_skip_code);
> link(r):=q; q:=r;
> end
>
> @ @<Append the new box to the current vertical list...@>=
> append_to_vlist(just_box);
> if adjust_head<>adjust_tail then
> begin link(tail):=link(adjust_head); tail:=adjust_tail;
> end;
> adjust_tail:=null
14198,14199c17316,17317
< begin cur_width←second_width; cur_indent←second_indent;
< end
---
> begin cur_width:=second_width; cur_indent:=second_indent;
> end
14201,14215c17319,17325
< begin cur_width←first_width; cur_indent←first_indent;
< end
< else begin cur_width←mem[par_shape_ptr+2*cur_line].sc;
< cur_indent←mem[par_shape_ptr+2*cur_line-1].sc;
< end;
< just_box←hpack(q,cur_width,exactly);
< shift_amount(just_box)←cur_indent
<
< @ @<Append the new box to the current vertical list...@>=
< append_to_vlist(just_box);
< if adjustments≠null then
< begin link(tail)←adjustments;
< repeat tail←link(tail);
< until link(tail)=null;
< end
---
> begin cur_width:=first_width; cur_indent:=first_indent;
> end
> else begin cur_width:=mem[par_shape_ptr+2*cur_line].sc;
> cur_indent:=mem[par_shape_ptr+2*cur_line-1].sc;
> end;
> adjust_tail:=adjust_head; just_box:=hpack(q,cur_width,exactly);
> shift_amount(just_box):=cur_indent
14217,14218c17327,17328
< @ Penalties between the lines of a paragraph come from widow lines, from
< the |inter_line_penalty| parameter, and from lines that end at
---
> @ Penalties between the lines of a paragraph come from club and widow lines,
> from the |inter_line_penalty| parameter, and from lines that end at
14220,14221c17330,17332
< two contributions of widow-line penalties. The local variable |pen| will
< be set to the sum of all relevant penalties for the current line.
---
> both club-line and widow-line penalties. The local variable |pen| will
> be set to the sum of all relevant penalties for the current line, except
> that the final line is never penalized.
14224,14232c17335,17346
< if cur_line+1=best_line then pen←0@+else pen←inter_line_penalty;
< if (cur_line=already+1)∧(best_line≠already+2) then pen←pen+widow_penalty;
< if cur_line+2=best_line then pen←pen+final_widow_penalty;
< if disc_break then pen←pen+broken_penalty;
< if pen≠0 then
< begin r←new_penalty(pen);
< link(tail)←r; tail←r;
< end
< @* \[39] Pre-hyphenation.
---
> if cur_line+1<>best_line then
> begin pen:=inter_line_penalty;
> if cur_line=prev_graf+1 then pen:=pen+club_penalty;
> if cur_line+2=best_line then pen:=pen+final_widow_penalty;
> if disc_break then pen:=pen+broken_penalty;
> if pen<>0 then
> begin r:=new_penalty(pen);
> link(tail):=r; tail:=r;
> end;
> end
>
> @* \[40] Pre-hyphenation.
14248,14268c17362,17385
< This is a sequence of nodes $p↓0p↓1\ldotsm p↓m$ where $p↓0$ is a glue
< node, $p↓1\ldotsm p↓{m-1}$ are either character or ligature or kern nodes,
< and $p↓m$ is a glue or penalty or insertion or adjust or mark node.
< (Therefore hyphenation is disabled by boxes, math formulas, whatsit nodes, and
< discretionary nodes already inserted by the user.) The ligature nodes among
< $p↓1\ldotsm p↓{m-1}$ are effectively expanded into the original non-ligature
< characters, and the kern nodes are ignored. Each character $c$ is now
< classified as either a nonletter (if |c≥128| or |lc_code(c)=0|), a lower
< case letter (if |lc_code(c)=c|), or an upper case letter (otherwise); an
< upper case letter is treated as if it were |lc_code(c)| for purposes of
< hyphenation. The characters generated by $p↓1\ldotsm p↓{m-1}$ may begin with
< nonletters; let $c↓1$ be the first letter that is not in the middle
< of a ligature. All characters that do not have
< the same font as $c↓1$ will be treated as nonletters. \TeX\ looks ahead
< for as many consecutive letters $c↓1\ldotsm c↓n$ as possible; however,
< |n| must be less than 64, so a character that would otherwise be $c↓{64}$
< is effectively not a letter. Furthermore $c↓n$ must not be in the middle
< of a ligature. In this way we obtain a string of letters
< $c↓1\ldotsm c↓n$ that are generated by nodes $p↓a\ldotsm p↓b$, where
< |1≤a≤b+1≤m|. If |n≥5|, this string qualifies for hyphenation; however,
< |uc_hyph| must be nonzero, if $c↓1$ is upper case.
---
> This is a sequence of nodes $p_0p_1\ldots p_m$ where $p_0$ is a glue node,
> $p_1\ldots p_{m-1}$ are either character or ligature or whatsit or
> implicit kern nodes, and $p_m$ is a glue or penalty or insertion or adjust
> or mark or whatsit or explicit kern node. (Therefore hyphenation is
> disabled by boxes, math formulas, and discretionary nodes already inserted
> by the user.) The ligature nodes among $p_1\ldots p_{m-1}$ are effectively
> expanded into the original non-ligature characters; the kern nodes and
> whatsits are ignored. Each character |c| is now classified as either a
> nonletter (if |lc_code(c)=0|), a lowercase letter (if
> |lc_code(c)=c|), or an uppercase letter (otherwise); an uppercase letter
> is treated as if it were |lc_code(c)| for purposes of hyphenation. The
> characters generated by $p_1\ldots p_{m-1}$ may begin with nonletters; let
> $c_1$ be the first letter that is not in the middle of a ligature. Whatsit
> nodes preceding $c_1$ are ignored; a whatsit found after $c_1$ will be the
> terminating node $p_m$. All characters that do not have the same font as
> $c_1$ will be treated as nonletters. The |hyphen_char| for that font
> must be between 0 and 255, otherwise hyphenation will not be attempted.
> \TeX\ looks ahead for as many consecutive letters $c_1\ldots c_n$ as
> possible; however, |n| must be less than 64, so a character that would
> otherwise be $c_{64}$ is effectively not a letter. Furthermore $c_n$ must
> not be in the middle of a ligature. In this way we obtain a string of
> letters $c_1\ldots c_n$ that are generated by nodes $p_a\ldots p_b$, where
> |1<=a<=b+1<=m|. If |n>=l_hyf+r_hyf|, this string qualifies for hyphenation;
> however, |uc_hyph| must be positive, if $c_1$ is uppercase.
14271c17388
< sequence $c↓1\ldotsm c↓n$ is found; then potential positions for hyphens
---
> sequence $c_1\ldots c_n$ is found; then potential positions for hyphens
14273c17390
< $p↓a\ldotsm p↓b$ are replaced by a new sequence of nodes that includes the
---
> $p_a\ldots p_b$ are replaced by a new sequence of nodes that includes the
14284c17401,17406
< @ The letters $c↓1\ldotsm c↓n$ that are candidates for hyphenation are placed
---
> @<Initialize for hyphenating...@>=
> begin @!init if trie_not_ready then init_trie;@+tini@;@/
> cur_lang:=init_cur_lang; l_hyf:=init_l_hyf; r_hyf:=init_r_hyf;
> end
>
> @ The letters $c_1\ldots c_n$ that are candidates for hyphenation are placed
14286c17408
< nodes $p↓a$ and@@$p↓b$ in the description above are placed into variables
---
> nodes $p_{a-1}$ and~$p_b$ in the description above are placed into variables
14290c17412
< @!hc:array[0..65] of ascii_code; {word to be hyphenated}
---
> @!hc:array[0..65] of 0..256; {word to be hyphenated}
14294c17416,17420
< @!hu:array[1..63] of ascii_code; {like |hc|, before conversion to lower case}
---
> @!hu:array[0..63] of 0..256; {like |hc|, before conversion to lowercase}
> @!hyf_char:integer; {hyphen character of the relevant font}
> @!cur_lang,@!init_cur_lang:ASCII_code; {current hyphenation table of interest}
> @!l_hyf,@!r_hyf,@!init_l_hyf,@!init_r_hyf:integer; {limits on fragment sizes}
> @!hyf_bchar:halfword; {boundary character after $c_n$}
14299c17425
< @!i,@!j,@!l:small_number; {indices into |hc| or |hu|}
---
> @!j:small_number; {an index into |hc| or |hu|}
14306,14315c17432,17441
< begin s←link(cur_p);
< if s≠null then
< begin @<Skip to node |ha|, or |goto done1| if no hyphenation
< should be attempted@>;
< @<Skip to node |hb|, putting letters into |hu| and |hc|,
< or |goto done1| if no hyphenation should be attempted@>;
< @<Check that the nodes following |hb| permit hyphenation and that
< at least five letters have been found, otherwise |goto done1|@>;
< hyphenate;
< end;
---
> begin prev_s:=cur_p; s:=link(prev_s);
> if s<>null then
> begin @<Skip to node |ha|, or |goto done1| if no hyphenation
> should be attempted@>;
> if l_hyf+r_hyf>63 then goto done1;
> @<Skip to node |hb|, putting letters into |hu| and |hc|@>;
> @<Check that the nodes following |hb| permit hyphenation and that at least
> |l_hyf+r_hyf| letters have been found, otherwise |goto done1|@>;
> hyphenate;
> end;
14321,14323c17447,17449
< label done,found,not_found,found1,exit;
< var@?@<Local variables for hyphenation@>@;
< begin @<Find hyphen locations for the word in |hc|@>;
---
> label common_ending,done,found,found1,found2,not_found,exit;
> var @<Local variables for hyphenation@>@;
> begin @<Find hyphen locations for the word in |hc|, or |return|@>;
14326c17452
< the discretionary hyphens@>;
---
> the discretionary hyphens@>;
14329c17455
< @ The first thing we need to do is find the node |ha| that contains the
---
> @ The first thing we need to do is find the node |ha| just before the
14333,14346c17459,17480
< loop@+ begin if is_char_node(s) then
< begin c←qo(character(s)); hf←font(s);
< end
< else if type(s)=ligature_node then
< begin q←lig_ptr(s); c←qo(character(q)); hf←font(q);
< end
< else if type(s)≠kern_node then goto done1
< else c←128;
< if c<128 then if lc_code(c)≠0 then
< if (lc_code(c)=c)∨(uc_hyph≠0) then goto done2
< else goto done1;
< s←link(s);
< end;
< done2: ha←s
---
> loop@+ begin if is_char_node(s) then
> begin c:=qo(character(s)); hf:=font(s);
> end
> else if type(s)=ligature_node then
> if lig_ptr(s)=null then goto continue
> else begin q:=lig_ptr(s); c:=qo(character(q)); hf:=font(q);
> end
> else if (type(s)=kern_node)and(subtype(s)=normal) then goto continue
> else if type(s)=whatsit_node then
> begin @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>;
> goto continue;
> end
> else goto done1;
> if lc_code(c)<>0 then
> if (lc_code(c)=c)or(uc_hyph>0) then goto done2
> else goto done1;
> continue: prev_s:=s; s:=link(prev_s);
> end;
> done2: hyf_char:=hyphen_char[hf];
> if hyf_char<0 then goto done1;
> if hyf_char>255 then goto done1;
> ha:=prev_s
14351,14364c17485,17502
< hn←0;
< loop@+ begin if is_char_node(s) then
< begin if font(s)≠hf then goto done3;
< c←qo(character(s));
< if c≥128 then goto done3;
< if (lc_code(c)=0)∨(hn=63) then goto done3;
< hb←s; incr(hn); hu[hn]←c; hc[hn]←lc_code(c);
< end
< else if type(s)=ligature_node then
< @<Move the characters of a ligature node to |hu| and |hc|;
< but |goto done3| if they are not all letters@>
< else if type(s)≠kern_node then goto done3;
< s←link(s);
< end;
---
> hn:=0;
> loop@+ begin if is_char_node(s) then
> begin if font(s)<>hf then goto done3;
> hyf_bchar:=character(s); c:=qo(hyf_bchar);
> if lc_code(c)=0 then goto done3;
> if hn=63 then goto done3;
> hb:=s; incr(hn); hu[hn]:=c; hc[hn]:=lc_code(c); hyf_bchar:=non_char;
> end
> else if type(s)=ligature_node then
> @<Move the characters of a ligature node to |hu| and |hc|;
> but |goto done3| if they are not all letters@>
> else if (type(s)=kern_node)and(subtype(s)=normal) then
> begin hb:=s;
> hyf_bchar:=font_bchar[hf];
> end
> else goto done3;
> s:=link(s);
> end;
14373,14381c17511,17521
< begin j←hn; q←lig_ptr(s);
< if font(q)≠hf then goto done3;
< repeat c←qo(character(q));
< if c≥128 then goto done3;
< if (lc_code(c)=0)∨(j=63) then goto done3;
< incr(j); hu[j]←c; hc[j]←lc_code(c);@/
< q←link(q);
< until q=null;
< hb←s; hn←j;
---
> begin if font(lig_char(s))<>hf then goto done3;
> j:=hn; q:=lig_ptr(s);@+if q>null then hyf_bchar:=character(q);
> while q>null do
> begin c:=qo(character(q));
> if lc_code(c)=0 then goto done3;
> if j=63 then goto done3;
> incr(j); hu[j]:=c; hc[j]:=lc_code(c);@/
> q:=link(q);
> end;
> hb:=s; hn:=j;
> if odd(subtype(s)) then hyf_bchar:=font_bchar[hf]@+else hyf_bchar:=non_char;
14385,14394c17525,17535
< if hn<5 then goto done1;
< loop@+ begin if ¬(is_char_node(s)) then
< case type(s) of
< ligature_node,kern_node: do_nothing;
< glue_node,penalty_node,ins_node,adjust_node,mark_node:
< goto done4;
< othercases goto done1
< endcases;
< s←link(s);
< end;
---
> if hn<l_hyf+r_hyf then goto done1; {|l_hyf| and |r_hyf| are |>=1|}
> loop@+ begin if not(is_char_node(s)) then
> case type(s) of
> ligature_node: do_nothing;
> kern_node: if subtype(s)<>normal then goto done4;
> whatsit_node,glue_node,penalty_node,ins_node,adjust_node,mark_node:
> goto done4;
> othercases goto done1
> endcases;
> s:=link(s);
> end;
14396c17537,17538
< @* \[40] Post-hyphenation.
---
>
> @* \[41] Post-hyphenation.
14405a17548,17550
> @!init_list:pointer; {list of punctuation characters preceding the word}
> @!init_lig:boolean; {does |init_list| represent a ligature?}
> @!init_lft:boolean; {if so, did the ligature involve a left boundary?}
14408c17553
< @!i,@!j,@!l:small_number; {indices into |hc| or |hu|}
---
> @!i,@!j,@!l:0..65; {indices into |hc| or |hu|}
14409a17555
> @!bchar:halfword; {right boundary character of hyphenated word, or |non_char|}
14411,14414c17557,17562
< @ \TeX\ will never insert a hyphen that has fewer than two letters before
< it or fewer than three after it; hence, a five-letter word has comparatively
< little chance of being hyphenated. If no hyphens have been found,
< we can save time by not having to make any changes to the paragraph.
---
> @ \TeX\ will never insert a hyphen that has fewer than
> \.{\\lefthyphenmin} letters before it or fewer than
> \.{\\righthyphenmin} after it; hence, a short word has
> comparatively little chance of being hyphenated. If no hyphens have
> been found, we can save time by not having to make any changes to the
> paragraph.
14417c17565
< for j←2 to hn-3 do if odd(hyf[j]) then goto found1;
---
> for j:=l_hyf to hn-r_hyf do if odd(hyf[j]) then goto found1;
14422,14423c17570,17577
< subsequence of nodes |ha..hb|. The variable |s| will point to the node
< preceding |ha|, and |q| will point to the node following |hb|, so that
---
> subsequence of nodes between |ha| and~|hb|. An attempt is made to
> preserve the effect that implicit boundary characters and punctuation marks
> had on ligatures inside the hyphenated word, by storing a left boundary or
> preceding character in |hu[0]| and by storing a possible right boundary
> in |bchar|. We set |j:=0| if |hu[0]| is to be part of the reconstruction;
> otherwise |j:=1|.
> The variable |s| will point to the tail of the current hlist, and
> |q| will point to the node following |hb|, so that
14427,14430c17581,17606
< q←link(hb); link(hb)←null; s←cur_p;
< while link(s)≠ha do s←link(s);
< link(s)←null; flush_node_list(ha);
< @<Reconstitute nodes for the hyphenated word, inserting discretionary hyphens@>
---
> q:=link(hb); link(hb):=null; r:=link(ha); link(ha):=null; bchar:=hyf_bchar;
> if is_char_node(ha) then
> if font(ha)<>hf then goto found2
> else begin init_list:=ha; init_lig:=false; hu[0]:=qo(character(ha));
> end
> else if type(ha)=ligature_node then
> if font(lig_char(ha))<>hf then goto found2
> else begin init_list:=lig_ptr(ha); init_lig:=true; init_lft:=(subtype(ha)>1);
> hu[0]:=qo(character(lig_char(ha)));
> if init_list=null then if init_lft then
> begin hu[0]:=256; init_lig:=false;
> end; {in this case a ligature will be reconstructed from scratch}
> free_node(ha,small_node_size);
> end
> else begin {no punctuation found; look for left boundary}
> if not is_char_node(r) then if type(r)=ligature_node then
> if subtype(r)>1 then goto found2;
> j:=1; s:=ha; init_list:=null; goto common_ending;
> end;
> s:=cur_p; {we have |cur_p<>ha| because |type(cur_p)=glue_node|}
> while link(s)<>ha do s:=link(s);
> j:=0; goto common_ending;
> found2: s:=ha; j:=0; hu[0]:=256; init_lig:=false; init_list:=null;
> common_ending: flush_node_list(r);
> @<Reconstitute nodes for the hyphenated word, inserting discretionary hyphens@>;
> flush_list(init_list)
14433c17609
< {\def\!{\kern-1pt}
---
> {\def\!{\kern-1pt}%
14435,14447c17611,17633
< because ligatures might change when a hyphen is present. The manual discusses
< the difficulties of the word ``difficult'', but since fonts can include
< highly general ligatures, the discretionary material surrounding a hyphen can
< be even more complex than that. For example, suppose that \.{abcdef} is a
< word in a font for which the only ligatures are \.{b\!c}, \.{c\!d}, \.{d\!e},
< and \.{e\!f}. If this word is to permit hyphenation between \.b and \.c, the
< two patterns with and without hyphenation are $\.a\,\.{b\!c}\,\.{d\!e}\,\.f$
< and $\.a\,\.b\,\.-\,\.{c\!d}\,\.{e\!f}$. Thus the insertion of a hyphen might
< cause effects to ripple arbitrarily far into the rest of the word. A
< further complication arises if additional hyphens appear together with
< such rippling, e.g., if the word in the example just given could also be
< hyphenated between \.c and \.d; \TeX\ avoids this by simply ignoring the
< additional hyphens in such weird cases.}
---
> because ligatures might change when a hyphen is present. {\sl The \TeX book\/}
> discusses the difficulties of the word ``difficult'', and
> the discretionary material surrounding a
> hyphen can be considerably more complex than that. Suppose
> \.{abcdef} is a word in a font for which the only ligatures are \.{b\!c},
> \.{c\!d}, \.{d\!e}, and \.{e\!f}. If this word permits hyphenation
> between \.b and \.c, the two patterns with and without hyphenation are
> $\.a\,\.b\,\.-\,\.{c\!d}\,\.{e\!f}$ and $\.a\,\.{b\!c}\,\.{d\!e}\,\.f$.
> Thus the insertion of a hyphen might cause effects to ripple arbitrarily
> far into the rest of the word. A further complication arises if additional
> hyphens appear together with such rippling, e.g., if the word in the
> example just given could also be hyphenated between \.c and \.d; \TeX\
> avoids this by simply ignoring the additional hyphens in such weird cases.}
>
> Still further complications arise in the presence of ligatures that do not
> delete the original characters. When punctuation precedes the word being
> hyphenated, \TeX's method is not perfect under all possible scenarios,
> because punctuation marks and letters can propagate information back and forth.
> For example, suppose the original pre-hyphenation pair
> \.{*a} changes to \.{*y} via a \.{\?=:} ligature, which changes to \.{xy}
> via a \.{=:\?} ligature; if $p_{a-1}=\.x$ and $p_a=\.y$, the reconstitution
> procedure isn't smart enough to obtain \.{xy} again. In such cases the
> font designer should include a ligature that goes from \.{xa} to \.{xy}.
14450,14472c17636,17668
< an index@@|j|, this function creates a node for the next character or ligature
< found in the |hu| array starting at |hu[j]|, using font |hf|. For example,
< if |hu[j..j+2]| contains the three letters `f', `i', and `x', and if
< font |hf| contains an `fi' ligature but no `fix' ligature, then |reconstitute|
< will create a ligature node for `fi'. The index of the last character
< consumed, in this case |j+1|, will be returned. Furthermore, a kern node
< is created and appended, if kerning is called for between the consumed
< character or ligature and the next (unconsumed) character.
<
< A second parameter, |n|, gives the limit beyond which this procedure does not
< advance. In other words, |hu[n]| might be consumed, but |hu[n+1]| is never
< accessed.
<
< The global variable |hyphen_passed| is set to@@|k| if this procedure
< consumes two characters |hu[k]| and |hu[k+1]| such that |hyf[k]| is odd,
< i.e., if the ligature might have to be broken by a hyphen, or if a kern is
< inserted between |hu[k]| and |hu[k+1]|. If this condition holds for more
< than one value of |k|, the smallest value is used; and if the condition
< holds for no values of |k|, |hyphen_passed| is set to zero.
<
< After |reconstitute| has acted, |link(hold_head)| points to the character
< or ligature node that was created, and |link(link(hold_head))| will either
< be |null| or a pointer to the kern node that was appended.
---
> a string of characters $x_j\ldots x_n$, there is a smallest index $m\ge j$
> such that the ``translation'' of $x_j\ldots x_n$ by ligatures and kerning
> has the form $y_1\ldots y_t$ followed by the translation of $x_{m+1}\ldots x_n$,
> where $y_1\ldots y_t$ is some nonempty sequence of character, ligature, and
> kern nodes. We call $x_j\ldots x_m$ a ``cut prefix'' of $x_j\ldots x_n$.
> For example, if $x_1x_2x_3=\.{fly}$, and if the font contains `fl' as a
> ligature and a kern between `fl' and `y', then $m=2$, $t=2$, and $y_1$ will
> be a ligature node for `fl' followed by an appropriate kern node~$y_2$.
> In the most common case, $x_j$~forms no ligature with $x_{j+1}$ and we
> simply have $m=j$, $y_1=x_j$. If $m<n$ we can repeat the procedure on
> $x_{m+1}\ldots x_n$ until the entire translation has been found.
>
> The |reconstitute| function returns the integer $m$ and puts the nodes
> $y_1\ldots y_t$ into a linked list starting at |link(hold_head)|,
> getting the input $x_j\ldots x_n$ from the |hu| array. If $x_j=256$,
> we consider $x_j$ to be an implicit left boundary character; in this
> case |j| must be strictly less than~|n|. There is a
> parameter |bchar|, which is either 256 or an implicit right boundary character
> assumed to be present just following~$x_n$. (The value |hu[n+1]| is never
> explicitly examined, but the algorithm imagines that |bchar| is there.)
>
> If there exists an index |k| in the range $j\le k\le m$ such that |hyf[k]|
> is odd and such that the result of |reconstitute| would have been different
> if $x_{k+1}$ had been |hchar|, then |reconstitute| sets |hyphen_passed|
> to the smallest such~|k|. Otherwise it sets |hyphen_passed| to zero.
>
> A special convention is used in the case |j=0|: Then we assume that the
> translation of |hu[0]| appears in a special list of charnodes starting at
> |init_list|; moreover, if |init_lig| is |true|, then |hu[0]| will be
> a ligature character, involving a left boundary if |init_lft| is |true|.
> This facility is provided for cases when a hyphenated
> word is preceded by punctuation (like single or double quotes) that might
> affect the translation of the beginning of the word.
14478,14479c17674,17675
< function reconstitute(@!j,@!n:small_number):
< small_number;
---
> function reconstitute(@!j,@!n:small_number;@!bchar,@!hchar:halfword):
> small_number;
14481,14482c17677,17678
< var p:pointer; {a node being created}
< @!s:pointer; {a node being appended to}
---
> var @!p:pointer; {temporary register for list manipulation}
> @!t:pointer; {a node being appended to}
14484,14485c17680,17681
< @!c:quarterword; {current character}
< @!d:quarterword; {current character or ligature}
---
> @!cur_rh:halfword; {hyphen character for ligature testing}
> @!test_char:halfword; {hyphen or other character for ligature testing}
14487,14503c17683,17709
< @!r:0..font_mem_size; {position of current lig/kern instruction}
< begin @<Build a list of characters in a maximal ligature, and set |w|
< to the amount of kerning that should follow@>;
< @<If the list has more than one element, create a ligature node@>;
< @<Attach kerning, if |w≠0|@>;
< reconstitute←j;
< end;
<
< @ @<Build a list of characters in a maximal ligature...@>=
< hyphen_passed←0; s←hold_head; w←0; d←qi(hu[j]); c←d;
< loop@+ begin continue: p←get_avail; font(p)←hf;
< character(p)←c; link(s)←p;@/
< @<Look for a ligature or kern between |d| and the following
< character; update the data structure and |goto continue|
< if a ligature is found, otherwise update@@|w| and |goto done|@>;
< end;
< done:
---
> @!k:font_index; {position of current lig/kern instruction}
> begin hyphen_passed:=0; t:=hold_head; w:=0; link(hold_head):=null;
> {at this point |ligature_present=lft_hit=rt_hit=false|}
> @<Set up data structures with the cursor following position |j|@>;
> continue:@<If there's a ligature or kern at the cursor position, update the data
> structures, possibly advancing~|j|; continue until the cursor moves@>;
> @<Append a ligature and/or kern to the translation;
> |goto continue| if the stack of inserted ligatures is nonempty@>;
> reconstitute:=j;
> end;
>
> @ The reconstitution procedure shares many of the global data structures
> by which \TeX\ has processed the words before they were hyphenated.
> There is an implied ``cursor'' between characters |cur_l| and |cur_r|;
> these characters will be tested for possible ligature activity. If
> |ligature_present| then |cur_l| is a ligature character formed from the
> original characters following |cur_q| in the current translation list.
> There is a ``ligature stack'' between the cursor and character |j+1|,
> consisting of pseudo-ligature nodes linked together by their |link| fields.
> This stack is normally empty unless a ligature command has created a new
> character that will need to be processed later. A pseudo-ligature is
> a special node having a |character| field that represents a potential
> ligature and a |lig_ptr| field that points to a |char_node| or is |null|.
> We have
> $$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack>null|;\cr
> |qi(hu[j+1])|,&if |lig_stack=null| and |j<n|;\cr
> bchar,&if |lig_stack=null| and |j=n|.\cr}$$
14505,14534c17711,17774
< @ @<Look for a ligature or kern between |d| and...@>=
< if j=n then goto done;
< q←char_info(hf)(d);
< if char_tag(q)≠lig_tag then goto done;
< r←lig_kern_start(hf)(q); c←qi(hu[j+1]);
< loop@+ begin q←font_info[r].qqqq;
< if next_char(q)=c then
< begin if odd(hyf[j])∧(hyphen_passed=0) then hyphen_passed←j;
< if op_bit(q)<kern_flag then
< @<Append to the ligature and |goto continue|@>
< else begin w←char_kern(hf)(q);
< goto done;
< end;
< end
< else if stop_bit(q)<stop_flag then incr(r)
< else goto done;
< end
<
< @ @<Append to the ligature...@>=
< begin d←rem_byte(q);
< incr(j); s←p; goto continue;
< end
<
< @ After the list has been built, |link(s)| points to the final list element.
<
< @<If the list has more than one element, create a ligature node@>=
< if s≠hold_head then
< begin p←new_ligature(hf,d,link(hold_head));
< link(hold_head)←p;
< end
---
> @<Glob...@>=
> @!cur_l,@!cur_r:halfword; {characters before and after the cursor}
> @!cur_q:pointer; {where a ligature should be detached}
> @!lig_stack:pointer; {unfinished business to the right of the cursor}
> @!ligature_present:boolean; {should a ligature node be made for |cur_l|?}
> @!lft_hit,@!rt_hit:boolean; {did we hit a ligature with a boundary character?}
>
> @ @d append_charnode_to_t(#)== begin link(t):=get_avail; t:=link(t);
> font(t):=hf; character(t):=#;
> end
> @d set_cur_r==begin if j<n then cur_r:=qi(hu[j+1])@+else cur_r:=bchar;
> if odd(hyf[j]) then cur_rh:=hchar@+else cur_rh:=non_char;
> end
>
> @<Set up data structures with the cursor following position |j|@>=
> cur_l:=qi(hu[j]); cur_q:=t;
> if j=0 then
> begin ligature_present:=init_lig; p:=init_list;
> if ligature_present then lft_hit:=init_lft;
> while p>null do
> begin append_charnode_to_t(character(p)); p:=link(p);
> end;
> end
> else if cur_l<non_char then append_charnode_to_t(cur_l);
> lig_stack:=null; set_cur_r
>
> @ We may want to look at the lig/kern program twice, once for a hyphen
> and once for a normal letter. (The hyphen might appear after the letter
> in the program, so we'd better not try to look for both at once.)
>
> @<If there's a ligature or kern at the cursor position, update...@>=
> if cur_l=non_char then
> begin k:=bchar_label[hf];
> if k=non_address then goto done@+else q:=font_info[k].qqqq;
> end
> else begin q:=char_info(hf)(cur_l);
> if char_tag(q)<>lig_tag then goto done;
> k:=lig_kern_start(hf)(q); q:=font_info[k].qqqq;
> if skip_byte(q)>stop_flag then
> begin k:=lig_kern_restart(hf)(q); q:=font_info[k].qqqq;
> end;
> end; {now |k| is the starting address of the lig/kern program}
> if cur_rh<non_char then test_char:=cur_rh@+else test_char:=cur_r;
> loop@+begin if next_char(q)=test_char then if skip_byte(q)<=stop_flag then
> if cur_rh<non_char then
> begin hyphen_passed:=j; hchar:=non_char; cur_rh:=non_char;
> goto continue;
> end
> else begin if hchar<non_char then if odd(hyf[j]) then
> begin hyphen_passed:=j; hchar:=non_char;
> end;
> if op_byte(q)<kern_flag then
> @<Carry out a ligature replacement, updating the cursor structure
> and possibly advancing~|j|; |goto continue| if the cursor doesn't
> advance, otherwise |goto done|@>;
> w:=char_kern(hf)(q); goto done; {this kern will be inserted below}
> end;
> if skip_byte(q)>=stop_flag then
> if cur_rh=non_char then goto done
> else begin cur_rh:=non_char; goto continue;
> end;
> k:=k+qo(skip_byte(q))+1; q:=font_info[k].qqqq;
> end;
> done:
14536,14537c17776,17836
< @ @<Attach kerning, if |w≠0|@>=
< if w≠0 then link(link(hold_head))←new_kern(w)
---
> @ @d wrap_lig(#)==if ligature_present then
> begin p:=new_ligature(hf,cur_l,link(cur_q));
> if lft_hit then
> begin subtype(p):=2; lft_hit:=false;
> end;
> if # then if lig_stack=null then
> begin incr(subtype(p)); rt_hit:=false;
> end;
> link(cur_q):=p; t:=p; ligature_present:=false;
> end
> @d pop_lig_stack==begin if lig_ptr(lig_stack)>null then
> begin link(t):=lig_ptr(lig_stack); {this is a charnode for |hu[j+1]|}
> t:=link(t); incr(j);
> end;
> p:=lig_stack; lig_stack:=link(p); free_node(p,small_node_size);
> if lig_stack=null then set_cur_r@+else cur_r:=character(lig_stack);
> end {if |lig_stack| isn't |null| we have |cur_rh=non_char|}
>
> @<Append a ligature and/or kern to the translation...@>=
> wrap_lig(rt_hit);
> if w<>0 then
> begin link(t):=new_kern(w); t:=link(t); w:=0;
> end;
> if lig_stack>null then
> begin cur_q:=t; cur_l:=character(lig_stack); ligature_present:=true;
> pop_lig_stack; goto continue;
> end
>
> @ @<Carry out a ligature replacement, updating the cursor structure...@>=
> begin if cur_l=non_char then lft_hit:=true;
> if j=n then if lig_stack=null then rt_hit:=true;
> check_interrupt; {allow a way out in case there's an infinite ligature loop}
> case op_byte(q) of
> qi(1),qi(5):begin cur_l:=rem_byte(q); {\.{=:\?}, \.{=:\?>}}
> ligature_present:=true;
> end;
> qi(2),qi(6):begin cur_r:=rem_byte(q); {\.{\?=:}, \.{\?=:>}}
> if lig_stack>null then character(lig_stack):=cur_r
> else begin lig_stack:=new_lig_item(cur_r);
> if j=n then bchar:=non_char
> else begin p:=get_avail; lig_ptr(lig_stack):=p;
> character(p):=qi(hu[j+1]); font(p):=hf;
> end;
> end;
> end;
> qi(3):begin cur_r:=rem_byte(q); {\.{\?=:\?}}
> p:=lig_stack; lig_stack:=new_lig_item(cur_r); link(lig_stack):=p;
> end;
> qi(7),qi(11):begin wrap_lig(false); {\.{\?=:\?>}, \.{\?=:\?>>}}
> cur_q:=t; cur_l:=rem_byte(q); ligature_present:=true;
> end;
> othercases begin cur_l:=rem_byte(q); ligature_present:=true; {\.{=:}}
> if lig_stack>null then pop_lig_stack
> else if j=n then goto done
> else begin append_charnode_to_t(cur_r); incr(j); set_cur_r;
> end;
> end
> endcases;
> if op_byte(q)>qi(4) then if op_byte(q)<>qi(7) then goto done;
> goto continue;
> end
14541,14542c17840,17841
< |hu[1..hn]| after node |s|, and node |q| should be appended to the result.
< During this process, the variable |i| will be a temporary counter or an
---
> |hu[1..hn]| after node |ha|, and node |q| should be appended to the result.
> During this process, the variable |i| will be a temporary
14546c17845
< we need two new local variables:
---
> we need a few new local variables:
14550c17849,17853
< discretionary branches being reconstructed}
---
> discretionary branches being reconstructed}
> @!c:ASCII_code; {character temporarily replaced by a hyphen}
> @!c_loc:0..63; {where that character came from}
> @!r_count:integer; {replacement count for discretionary}
> @!hyf_node:pointer; {the hyphen, if it exists}
14552,14553c17855
< @ When the following code is performed, |hyf[j]| will be zero for |j=1|
< and for |j≥hn-2|.
---
> @ When the following code is performed, |hyf[0]| and |hyf[hn]| will be zero.
14556,14575c17858,17889
< j←0;
< repeat l←j; j←reconstitute(j+1,hn);
< if hyphen_passed≠0 then
< @<Create and append a discretionary node as an alternative to the
< ligature, and continue to develop both branches until they
< become equivalent@>
< else begin link(s)←link(hold_head); s←link(s);
< if link(s)≠null then s←link(s);
< end;
< if odd(hyf[j]) then @<Insert a discretionary hyphen after |s|@>;
< until j=hn;
< link(s)←q
<
< @ @<Create and append a discretionary node as an alternative...@>=
< begin r←get_node(small_node_size);
< link(s)←r; link(r)←link(hold_head); type(r)←disc_node;
< major_tail←link(hold_head);
< if link(major_tail)≠null then major_tail←link(major_tail);
< i←hyphen_passed;
< @<Put the \(c)characters |hu[l+1..i]| and a hyphen into |pre_break(r)|@>;
---
> repeat l:=j; j:=reconstitute(j,hn,bchar,qi(hyf_char))+1;
> if hyphen_passed=0 then
> begin link(s):=link(hold_head);
> while link(s)>null do s:=link(s);
> if odd(hyf[j-1]) then
> begin l:=j; hyphen_passed:=j-1; link(hold_head):=null;
> end;
> end;
> if hyphen_passed>0 then
> @<Create and append a discretionary node as an alternative to the
> unhyphenated word, and continue to develop both branches until they
> become equivalent@>;
> until j>hn;
> link(s):=q
>
> @ In this repeat loop we will insert another discretionary if |hyf[j-1]| is
> odd, when both branches of the previous discretionary end at position |j-1|.
> Strictly speaking, we aren't justified in doing this, because we don't know
> that a hyphen after |j-1| is truly independent of those branches. But in almost
> all applications we would rather not lose a potentially valuable hyphenation
> point. (Consider the word `difficult', where the letter `c' is in position |j|.)
>
> @d advance_major_tail==begin major_tail:=link(major_tail); incr(r_count);
> end
>
> @<Create and append a discretionary node as an alternative...@>=
> repeat r:=get_node(small_node_size);
> link(r):=link(hold_head); type(r):=disc_node;
> major_tail:=r; r_count:=0;
> while link(major_tail)>null do advance_major_tail;
> i:=hyphen_passed; hyf[i]:=0;
> @<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>;
14577c17891
< list and to |major_tail| until synchronization has been achieved@>;
---
> list and to |major_tail| until synchronization has been achieved@>;
14579,14580c17893,17895
< appropriately@>;
< end
---
> appropriately@>;
> hyphen_passed:=j-1; link(hold_head):=null;
> until not odd(hyf[j-1])
14583c17898
< or kern. At this point we have |i≤j|.
---
> or kern. At this point we have |l-1<=i<j| and |i<hn|.
14585,14594c17900,17917
< @<Put the \(c)characters |hu[l+1..i]| and a hyphen into |pre_break(r)|@>=
< minor_tail←null; c←hu[i+1]; hu[i+1]←"-";
< repeat l←reconstitute(l+1,i+1);
< if minor_tail=null then pre_break(r)←link(hold_head)
< else link(minor_tail)←link(hold_head);
< minor_tail←link(hold_head);
< if link(minor_tail)≠null then minor_tail←link(minor_tail);
< until l>i;
< hu[i+1]←c; {restore the character in the hyphen position}
< decr(l); hyf[l]←0
---
> @<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>=
> minor_tail:=null; pre_break(r):=null; hyf_node:=new_character(hf,hyf_char);
> if hyf_node<>null then
> begin incr(i); c:=hu[i]; hu[i]:=hyf_char; free_avail(hyf_node);
> end;
> while l<=i do
> begin l:=reconstitute(l,i,font_bchar[hf],non_char)+1;
> if link(hold_head)>null then
> begin if minor_tail=null then pre_break(r):=link(hold_head)
> else link(minor_tail):=link(hold_head);
> minor_tail:=link(hold_head);
> while link(minor_tail)>null do minor_tail:=link(minor_tail);
> end;
> end;
> if hyf_node<>null then
> begin hu[i]:=c; {restore the character in the hyphen position}
> l:=i; decr(i);
> end
14596c17919
< @ The synchronization algorithm begins with |l≤j|.
---
> @ The synchronization algorithm begins with |l=i+1<=j|.
14599c17922,17925
< minor_tail←null; post_break(r)←null;
---
> minor_tail:=null; post_break(r):=null; c_loc:=0;
> if bchar_label[hf]<>non_address then {put left boundary at beginning of new line}
> begin decr(l); c:=hu[l]; c_loc:=l; hu[l]:=256;
> end;
14601,14632c17927,17960
< begin repeat l←reconstitute(l+1,hn);
< if minor_tail=null then post_break(r)←link(hold_head)
< else link(minor_tail)←link(hold_head);
< minor_tail←link(hold_head);
< if link(minor_tail)≠null then
< begin hyf[l]←0; minor_tail←link(minor_tail); {kern present}
< end;
< until l≥j;
< while l>j do
< begin j←reconstitute(j+1,hn);
< link(major_tail)←link(hold_head);
< major_tail←link(hold_head);
< if link(major_tail)≠null then
< begin hyf[j]←0; major_tail←link(major_tail); {kern present}
< end;
< end;
< end
<
< @ @<Move pointer |s| to the end of the current list...@>=
< i←0; s←r;
< while link(s)≠null do
< begin incr(i); s←link(s);
< end;
< replace_count(r)←i
<
< @ At this point |link(s)| is |null|.
<
< @<Insert a discretionary hyphen after |s|@>=
< begin r←new_disc; pre_break(r)←new_character(hf,"-");
< link(s)←r; s←r;
< end
< @* \[41] Hyphenation.
---
> begin repeat l:=reconstitute(l,hn,bchar,non_char)+1;
> if c_loc>0 then
> begin hu[c_loc]:=c; c_loc:=0;
> end;
> if link(hold_head)>null then
> begin if minor_tail=null then post_break(r):=link(hold_head)
> else link(minor_tail):=link(hold_head);
> minor_tail:=link(hold_head);
> while link(minor_tail)>null do minor_tail:=link(minor_tail);
> end;
> until l>=j;
> while l>j do
> @<Append characters of |hu[j..@,]| to |major_tail|, advancing~|j|@>;
> end
>
> @ @<Append characters of |hu[j..@,]|...@>=
> begin j:=reconstitute(j,hn,bchar,non_char)+1;
> link(major_tail):=link(hold_head);
> while link(major_tail)>null do advance_major_tail;
> end
>
> @ Ligature insertion can cause a word to grow exponentially in size. Therefore
> we must test the size of |r_count| here, even though the hyphenated text
> was at most 63 characters long.
>
> @<Move pointer |s| to the end of the current list...@>=
> if r_count>127 then {we have to forget the discretionary hyphen}
> begin link(s):=link(r); link(r):=null; flush_node_list(r);
> end
> else begin link(s):=r; replace_count(r):=r_count;
> end;
> s:=major_tail
>
> @* \[42] Hyphenation.
14636c17964
< using an algorithm due to Frank M. Liang.
---
> using an algorithm due to Frank~M. Liang.
14641c17969
< for all |j|, and invalid `\.{\^\^?}' characters are inserted into |hc[0]|
---
> for all |j|, and invalid characters are inserted into |hc[0]|
14644,14647c17972,17975
< |hc[0..(hn+1)]|. Each pattern $p↓1\ldotsm p↓k$ of length |k| has an associated
< sequence of |k+1| numbers $n↓0\ldotsm n↓k$; and if the pattern occurs in
< |hc[(j+1)..(j+k)]|, \TeX\ will set |hyf[j+i]←@tmax@>(hyf[j+i],@t$n↓i$@>)| for
< |0≤i≤k|. After this has been done for each pattern that occurs, a
---
> |hc[0..(hn+1)]|. Each pattern $p_1\ldots p_k$ of length |k| has an associated
> sequence of |k+1| numbers $n_0\ldots n_k$; and if the pattern occurs in
> |hc[(j+1)..(j+k)]|, \TeX\ will set |hyf[j+i]:=@tmax@>(hyf[j+i],@t$n_i$@>)| for
> |0<=i<=k|. After this has been done for each pattern that occurs, a
14651c17979
< The set of patterns $p↓1\ldotsm p↓k$ and associated numbers $n↓0\ldotsm n↓k$
---
> The set of patterns $p_1\ldots p_k$ and associated numbers $n_0\ldots n_k$
14656c17984
< 1982); \TeX\ simply starts with the patterns and works from there.
---
> 1983); \TeX\ simply starts with the patterns and works from there.
14660,14663c17988,17992
< Computer Program\-ming \bf3} (1973), 481--505]. We can find each pattern
< $p↓1\ldotsm p↓k$ by setting |@t$z↓1$@>←@t$p↓1$@>| and then, for |1<i≤k|,
< setting |@t$z↓i$@>←trie_link@t$(z↓{i-1})+p↓i$@>|; the pattern will be
< identified by the number $z↓k$. Since all the pattern information is
---
> Computer Programming \bf3} (1973), 481--505]. We can find each pattern
> $p_1\ldots p_k$ by letting $z_0$ be one greater than the relevant language
> index and then, for |1<=i<=k|,
> setting |@t$z_i$@>:=trie_link@t$(z_{i-1})+p_i$@>|; the pattern will be
> identified by the number $z_k$. Since all the pattern information is
14666,14668c17995,17997
< table is provided such that |trie_char@t$(z↓i)=p↓i$@>| for all |i|. There
< is also a table |trie_op|$(z↓k)$ to identify the numbers $n↓0\ldotsm n↓k$
< associated with $p↓1\ldotsm p↓k$.
---
> table is provided such that |trie_char@t$(z_i)=p_i$@>| for all |i|. There
> is also a table |trie_op|$(z_k)$ to identify the numbers $n_0\ldots n_k$
> associated with $p_1\ldots p_k$.
14670c17999
< Comparatively few different number sequences $n↓0\ldotsm n↓k$ actually occur,
---
> Comparatively few different number sequences $n_0\ldots n_k$ actually occur,
14672,14674c18001,18004
< are encoded in such a way that |trie_op|$(z↓k)$ is only one byte long.
< If |trie_op(@t$z↓k$@>)≠min_quarterword|, when $p↓1\ldotsm p↓k$ has matched
< the letters in |hc[(l-k+1)..l@,]|, we perform all of the required operations
---
> are encoded in such a way that |trie_op|$(z_k)$ is only one byte long.
> If |trie_op(@t$z_k$@>)<>min_quarterword|, when $p_1\ldots p_k$ has matched
> the letters in |hc[(l-k+1)..l@,]| of language |t|,
> we perform all of the required operations
14676,14678c18006,18008
< |v←trie_op(@t$z↓k$@>)|. Then set |hyf[l-hyf_distance[v]]←@tmax@>(
< hyf[l-hyf_distance[v]], hyf_num[v])|, and |v←hyf_next[v]|; repeat, if
< necessary, until |v=min_quarterword|.
---
> |v:=trie_op(@t$z_k$@>)|. Then set |v:=v+op_start[t]|,
> |hyf[l-hyf_distance[v]]:=@tmax@>(hyf[l-hyf_distance[v]], hyf_num[v])|,
> and |v:=hyf_next[v]|; repeat, if necessary, until |v=min_quarterword|.
14689,14691c18019,18022
< @!hyf_distance:array[quarterword] of small_number; {position |k-j| of $n↓j$}
< @!hyf_num:array[quarterword] of small_number; {value of $n↓j$}
< @!hyf_next:array[quarterword] of quarterword; {continuation of this |trie_op|}
---
> @!hyf_distance:array[1..trie_op_size] of small_number; {position |k-j| of $n_j$}
> @!hyf_num:array[1..trie_op_size] of small_number; {value of $n_j$}
> @!hyf_next:array[1..trie_op_size] of quarterword; {continuation code}
> @!op_start:array[ASCII_code] of 0..trie_op_size; {offset for current language}
14695c18026
< @!v:quarterword; {an index into |hyf_distance|, etc.}
---
> @!v:integer; {an index into |hyf_distance|, etc.}
14697,14700c18028,18031
< @ Assuming that these auxiliary tables have been set properly, the
< hyphenation algorithm is quite short. In the following code we use the
< fact that no pattern contains $p↓i=0$; setting |hc[hn+2]| to zero therefore
< guarantees that |hc[hn+3]| will never be fetched.
---
> @ Assuming that these auxiliary tables have been set up properly, the
> hyphenation algorithm is quite short. In the following code we set |hc[hn+2]|
> to the impossible value 256, in order to guarantee that |hc[hn+3]| will
> never be fetched.
14702,14703c18033,18034
< @<Find hyphen locations for the word in |hc|@>=
< for j←0 to hn do hyf[j]←0;
---
> @<Find hyphen locations for the word in |hc|...@>=
> for j:=0 to hn do hyf[j]:=0;
14705,14715c18036,18048
< |hyf| containing the hyphens) if an entry is found@>;
< hc[0]←127; hc[hn+1]←127; hc[hn+2]←0; {insert delimiters}
< for j←0 to hn-2 do
< begin z←hc[j]; l←j;
< while hc[l]=trie_char(z) do
< begin if trie_op(z)≠min_quarterword then
< @<Store \(m)maximum values in the |hyf| table@>;
< incr(l); z←trie_link(z)+hc[l];
< end;
< end;
< found: hyf[1]←0; hyf[hn-2]←0; hyf[hn-1]←0; hyf[hn]←0
---
> |hyf| containing the hyphens) if an entry is found@>;
> if trie_char(cur_lang+1)<>qi(cur_lang) then return; {no patterns for |cur_lang|}
> hc[0]:=0; hc[hn+1]:=0; hc[hn+2]:=256; {insert delimiters}
> for j:=0 to hn-r_hyf+1 do
> begin z:=trie_link(cur_lang+1)+hc[j]; l:=j;
> while hc[l]=qo(trie_char(z)) do
> begin if trie_op(z)<>min_quarterword then
> @<Store \(m)maximum values in the |hyf| table@>;
> incr(l); z:=trie_link(z)+hc[l];
> end;
> end;
> found: for j:=0 to l_hyf-1 do hyf[j]:=0;
> for j:=0 to r_hyf-1 do hyf[hn-j]:=0
14718,14721c18051,18054
< begin v←trie_op(z);
< repeat i←l-hyf_distance[v];
< if hyf_num[v]>hyf[i] then hyf[i]←hyf_num[v];
< v←hyf_next[v];
---
> begin v:=trie_op(z);
> repeat v:=v+op_start[cur_lang]; i:=l-hyf_distance[v];
> if hyf_num[v]>hyf[i] then hyf[i]:=hyf_num[v];
> v:=hyf_next[v];
14728,14732c18061,18065
< Journal\/\ \bf17} (1974), 135--142] using linear probing. If $\alpha$ and
< $\beta$ are words, we will say that $\alpha<\beta$ if $\leftv\alpha\rightv<
< \leftv\beta\rightv$ or if $\leftv\alpha\rightv=\leftv\beta\rightv$ and
< $\alpha$ is lexicographically smaller than $\beta$. (The notation $\leftv
< \alpha\rightv$ stands for the length of $\alpha$.) The idea of ordered hashing
---
> Journal\/ \bf17} (1974), 135--142] using linear probing. If $\alpha$ and
> $\beta$ are words, we will say that $\alpha<\beta$ if $\vert\alpha\vert<
> \vert\beta\vert$ or if $\vert\alpha\vert=\vert\beta\vert$ and
> $\alpha$ is lexicographically smaller than $\beta$. (The notation $\vert
> \alpha\vert$ stands for the length of $\alpha$.) The idea of ordered hashing
14734,14735c18067,18068
< a hash address $h=h(\alpha)$ and then looking in table positions $h$, $h-1$,
< $\ldotss$, until encountering the first word $\L\alpha$. If this word is
---
> a hash address $h=h(\alpha)$ and then looking in table positions |h|, |h-1|,
> \dots, until encountering the first word $\L\alpha$. If this word is
14739,14741c18072,18074
< in their |info| fields. The list for $c↓1\ldotsm c↓n$ contains $k$ if
< the word $c↓1\ldotsm c↓n$ has a discretionary hyphen between $c↓k$ and
< $c↓{k+1}$.
---
> in their |info| fields. The list for $c_1\ldots c_n$ contains the number |k| if
> the word $c_1\ldots c_n$ has a discretionary hyphen between $c_k$ and
> $c_{k+1}$.
14748c18081
< @!hyph_list:array[hyph_pointer] of pointer; {list of hyphen positions}
---
> @!hyph_list:array[hyph_pointer] of pointer; {lists of hyphen positions}
14755,14756c18088,18091
< for z←0 to hyph_size do hyph_word[z]←0;
< hyph_count←0;
---
> for z:=0 to hyph_size do
> begin hyph_word[z]:=0; hyph_list[z]:=null;
> end;
> hyph_count:=0;
14767c18102,18103
< find the word or we don't.
---
> find the word or we don't. Words from different languages are kept
> separate by appending the language code to the string.
14770,14777c18106,18113
< h←hc[1];
< for j←2 to hn do h←(h+h+hc[j]) mod hyph_size;
< loop@+ begin @<If the string |hyph_word[h]| is less than \(hc)|hc[1..hn]|,
< |goto not_found|; but if the two strings are equal,
< set |hyf| to the hyphen positions and |goto found|@>;
< if h>0 then decr(h)@+else h←hyph_size;
< end;
< not_found:
---
> h:=hc[1]; incr(hn); hc[hn]:=cur_lang;
> for j:=2 to hn do h:=(h+h+hc[j]) mod hyph_size;
> loop@+ begin @<If the string |hyph_word[h]| is less than \(hc)|hc[1..hn]|,
> |goto not_found|; but if the two strings are equal,
> set |hyf| to the hyphen positions and |goto found|@>;
> if h>0 then decr(h)@+else h:=hyph_size;
> end;
> not_found: decr(hn)
14780c18116
< k←hyph_word[h]; if k=0 then goto not_found;
---
> k:=hyph_word[h]; if k=0 then goto not_found;
14783,14790c18119,18126
< begin j←1; u←str_start[k];
< repeat if str_pool[u]<hc[j] then goto not_found;
< if str_pool[u]>hc[j] then goto done;
< incr(j); incr(u);
< until j>hn;
< @<Insert hyphens as specified in |hyph_list[h]|@>;
< goto found;
< end;
---
> begin j:=1; u:=str_start[k];
> repeat if so(str_pool[u])<hc[j] then goto not_found;
> if so(str_pool[u])>hc[j] then goto done;
> incr(j); incr(u);
> until j>hn;
> @<Insert hyphens as specified in |hyph_list[h]|@>;
> decr(hn); goto found;
> end;
14794,14797c18130,18140
< s←hyph_list[h];
< while s≠null do
< begin hyf[info(s)]←1; s←link(s);
< end
---
> s:=hyph_list[h];
> while s<>null do
> begin hyf[info(s)]:=1; s:=link(s);
> end
>
> @ @<Search |hyph_list| for pointers to |p|@>=
> for q:=0 to hyph_size do
> begin if hyph_list[q]=p then
> begin print_nl("HYPH("); print_int(q); print_char(")");
> end;
> end
14803,14804c18146,18151
< When \TeX\ has scanned the `\.{\\hyphenation}' control sequence, it calls
< on a procedure named |new_hyph_exceptions| to do the right thing.
---
> When \TeX\ has scanned `\.{\\hyphenation}', it calls on a procedure named
> |new_hyph_exceptions| to do the right thing.
>
> @d set_cur_lang==if language<=0 then cur_lang:=0
> else if language>255 then cur_lang:=0
> else cur_lang:=language
14807,14809c18154,18156
< label exit, found, not_found;
< var n:small_number; {length of current word}
< @!j:small_number; {an index into |hc|}
---
> label reswitch, exit, found, not_found;
> var n:0..64; {length of current word; not always a |small_number|}
> @!j:0..64; {an index into |hc|}
14816a18164
> set_cur_lang;
14818c18166
< until coming to a right brace; then skip an optional space and |return|@>;
---
> until coming to a right brace; then |return|@>;
14822,14834c18170,18183
< n←0; p←null;
< loop@+ begin get_nc_token;
< case cur_cmd of
< letter,other_char:@<Append a new letter or hyphen@>;
< spacer,right_brace: begin if n>4 then @<Enter a hyphenation exception@>;
< if cur_cmd=right_brace then
< begin @<Scan an optional space@>; return;
< end;
< n←0; p←null;
< end;
< othercases @<Give improper \.{\\hyphenation} error@>
< endcases;
< end
---
> n:=0; p:=null;
> loop@+ begin get_x_token;
> reswitch: case cur_cmd of
> letter,other_char,char_given:@<Append a new letter or hyphen@>;
> char_num: begin scan_char_num; cur_chr:=cur_val; cur_cmd:=char_given;
> goto reswitch;
> end;
> spacer,right_brace: begin if n>1 then @<Enter a hyphenation exception@>;
> if cur_cmd=right_brace then return;
> n:=0; p:=null;
> end;
> othercases @<Give improper \.{\\hyphenation} error@>
> endcases;
> end
14837,14839c18186
< begin help2("Hyphenation exceptions must contain only letters")@/
< ("and hyphens. But continue; I'll forgive and forget.");
< print_nl("! Improper \hyphenation will be flushed"); error;
---
> begin print_err("Improper "); print_esc("hyphenation");
14840a18188,18191
> print(" will be flushed");
> help2("Hyphenation exceptions must contain only letters")@/
> ("and hyphens. But continue; I'll forgive and forget.");
> error;
14845,14846c18196,18197
< else begin if lc_code(cur_chr)=0 then
< begin print_nl("! Not a letter");
---
> else begin if lc_code(cur_chr)=0 then
> begin print_err("Not a letter");
14848,14855c18199,18206
< help2("Letters in \hyphenation words must have \lccode>0.")@/
< ("Proceed; I'll ignore the character I just read.");
< error;
< end
< else if n<63 then
< begin incr(n); hc[n]←lc_code(cur_chr);
< end;
< end
---
> help2("Letters in \hyphenation words must have \lccode>0.")@/
> ("Proceed; I'll ignore the character I just read.");
> error;
> end
> else if n<63 then
> begin incr(n); hc[n]:=lc_code(cur_chr);
> end;
> end
14858,14860c18209,18211
< begin if n>1 then
< begin q←get_avail; link(q)←p; info(q)←n; p←q;
< end;
---
> begin if n<63 then
> begin q:=get_avail; link(q):=p; info(q):=n; p:=q;
> end;
14864,14872c18215,18220
< begin str_room(n); h←0;
< for j←1 to n do
< begin h←(h+h+hc[j]) mod hyph_size;
< append_char(hc[j]);
< end;
< s←make_string;
< while info(p)>n-3 do {eliminate hyphens \TeX\ doesn't like}
< begin q←link(p); free_avail(p); p←q;
< end;
---
> begin incr(n); hc[n]:=cur_lang; str_room(n); h:=0;
> for j:=1 to n do
> begin h:=(h+h+hc[j]) mod hyph_size;
> append_char(hc[j]);
> end;
> s:=make_string;
14877a18226
> @:TeX capacity exceeded exception dictionary}{\quad exception dictionary@>
14879,14884c18228,18233
< while hyph_word[h]≠0 do
< begin @<If the string |hyph_word[h]| is less than \(or)or equal to
< |s|, interchange |(hyph_word[h],hyph_list[h])| with |(s,p)|@>;
< if h>0 then decr(h)@+else h←hyph_size;
< end;
< hyph_word[h]←s; hyph_list[h]←p
---
> while hyph_word[h]<>0 do
> begin @<If the string |hyph_word[h]| is less than \(or)or equal to
> |s|, interchange |(hyph_word[h],hyph_list[h])| with |(s,p)|@>;
> if h>0 then decr(h)@+else h:=hyph_size;
> end;
> hyph_word[h]:=s; hyph_list[h]:=p
14887c18236
< k←hyph_word[h];
---
> k:=hyph_word[h];
14890c18239
< u←str_start[k]; v←str_start[s];
---
> u:=str_start[k]; v:=str_start[s];
14895,14896c18244,18245
< found:q←hyph_list[h]; hyph_list[h]←p; p←q;@/
< t←hyph_word[h]; hyph_word[h]←s; s←t;
---
> found:q:=hyph_list[h]; hyph_list[h]:=p; p:=q;@/
> t:=hyph_word[h]; hyph_word[h]:=s; s:=t;
14898c18247,18248
< @* \[42] Initializing the hyphenation tables.
---
>
> @* \[43] Initializing the hyphenation tables.
14900c18250
< patterns following a \.{\\patterns} speci\-fi\-ca\-tion. Such a specification
---
> patterns following a \.{\\patterns} specification. Such a specification
14905,14907c18255,18258
< The initialization first builds a trie that is linked instead of packed
< into sequential storage, so that insertions are readily made. Then it
< compresses the linked trie by identifying common subtries, and finally the
---
> The first step is to build a trie that is linked, instead of packed
> into sequential storage, so that insertions are readily made.
> After all patterns have been processed, \.{INITEX}
> compresses the linked trie by identifying common subtries. Finally the
14911c18262,18263
< @p init @<Declare procedures for preprocessing hyphenation patterns@>@;
---
> @<Declare subprocedures for |line_break|@>=
> @!init @<Declare procedures for preprocessing hyphenation patterns@>@;
14918c18270
< a pattern of length 5, with $n↓0\ldotsm n↓5=0\,0\,2\,0\,0\,1$ in the
---
> a pattern of length 5, with $n_0\ldots n_5=0\,0\,2\,0\,0\,1$ in the
14927,14928c18279,18280
< $$\hbox{|@t$v^\prime$@>←new_trie_op(0,1,min_quarterword)|,\qquad
< |v←new_trie_op(3,2,@t$v^\prime$@>)|.}$$
---
> $$\hbox{|@t$v^\prime$@>:=new_trie_op(0,1,min_quarterword)|,\qquad
> |v:=new_trie_op(3,2,@t$v^\prime$@>)|.}$$
14931c18283
< three have not appeared before.
---
> three have not appeared before for the current language.
14934,14938c18286
< is |trie_op_ptr|. If the table overflows, the excess ops are ignored,
< and |trie_op_ptr=max_quarterword|.
<
< @d quarterword_diff=max_quarterword-min_quarterword
< @d trie_op_hash_size=quarterword_diff+quarterword_diff {double}
---
> is |trie_op_ptr|.
14941,14943c18289,18297
< init@! trie_op_hash:array[0..trie_op_hash_size] of quarterword;
< {trie op codes for triples}
< @t\hskip1em@>@!trie_op_ptr:quarterword; {highest |trie_op| assigned}
---
> @!init@! trie_op_hash:array[-trie_op_size..trie_op_size] of 0..trie_op_size;
> {trie op codes for quadruples}
> @!trie_used:array[ASCII_code] of quarterword;
> {largest opcode used so far for this language}
> @!trie_op_lang:array[1..trie_op_size] of ASCII_code;
> {language part of a hashed quadruple}
> @!trie_op_val:array[1..trie_op_size] of quarterword;
> {opcode corresponding to a hashed quadruple}
> @!trie_op_ptr:0..trie_op_size; {number of stored ops so far}
14946,14949c18300,18305
< @ The hash function used by |new_trie_op| is based on the idea that
< 313/510 is an approximation to the golden ratio [cf.\ {\sl The Art of
< Computer Programming \bf3} (1973), 510--512]; but the choice is
< comparatively unimportant in this particular application.
---
> @ It's tempting to remove the |overflow| stops in the following procedure;
> |new_trie_op| could return |min_quarterword| (thereby simply ignoring
> part of a hyphenation pattern) instead of aborting the job. However, that would
> lead to different hyphenation results on different installations of \TeX\
> using the same patterns. The |overflow| stops are necessary for portability
> of patterns.
14954c18310
< var h:0..trie_op_hash_size; {trial hash location}
---
> var h:-trie_op_size..trie_op_size; {trial hash location}
14956,14972c18312,18334
< begin h←(n+313*d+361*v) mod trie_op_hash_size;
< loop@+ begin u←trie_op_hash[h];
< if u=min_quarterword then {empty position found}
< begin if trie_op_ptr≥max_quarterword-1 then {overflow}
< begin trie_op_ptr←max_quarterword;
< new_trie_op←min_quarterword; return;
< end;
< incr(trie_op_ptr); hyf_distance[trie_op_ptr]←d;
< hyf_num[trie_op_ptr]←n; hyf_next[trie_op_ptr]←v;
< trie_op_hash[h]←trie_op_ptr;
< new_trie_op←trie_op_ptr; return;
< end;
< if (hyf_distance[u]=d)∧(hyf_num[u]=n)∧(hyf_next[u]=v) then
< begin new_trie_op←u; return;
< end;
< if h>min_quarterword then decr(h)@+else h←max_quarterword;
< end;
---
> @!l:0..trie_op_size; {pointer to stored data}
> begin h:=abs(n+313*d+361*v+1009*cur_lang) mod (trie_op_size+trie_op_size)
> - trie_op_size;
> loop@+ begin l:=trie_op_hash[h];
> if l=0 then {empty position found for a new op}
> begin if trie_op_ptr=trie_op_size then
> overflow("pattern memory ops",trie_op_size);
> u:=trie_used[cur_lang];
> if u=max_quarterword then
> overflow("pattern memory ops per language",
> max_quarterword-min_quarterword);
> incr(trie_op_ptr); incr(u); trie_used[cur_lang]:=u;
> hyf_distance[trie_op_ptr]:=d;
> hyf_num[trie_op_ptr]:=n; hyf_next[trie_op_ptr]:=v;
> trie_op_lang[trie_op_ptr]:=cur_lang; trie_op_hash[h]:=trie_op_ptr;
> trie_op_val[trie_op_ptr]:=u; new_trie_op:=u; return;
> end;
> if (hyf_distance[l]=d)and(hyf_num[l]=n)and(hyf_next[l]=v)
> and(trie_op_lang[l]=cur_lang) then
> begin new_trie_op:=trie_op_val[l]; return;
> end;
> if h>-trie_op_size then decr(h)@+else h:=trie_op_size;
> end;
14974a18337,18361
> @ After |new_trie_op| has compressed the necessary opcode information,
> plenty of information is available to unscramble the data into the
> final form needed by our hyphenation algorithm.
>
> @<Sort \(t)the hyphenation op tables into proper order@>=
> op_start[0]:=-min_quarterword;
> for j:=1 to 255 do op_start[j]:=op_start[j-1]+qo(trie_used[j-1]);
> for j:=1 to trie_op_ptr do
> trie_op_hash[j]:=op_start[trie_op_lang[j]]+trie_op_val[j]; {destination}
> for j:=1 to trie_op_ptr do while trie_op_hash[j]>j do
> begin k:=trie_op_hash[j];@/
> t:=hyf_distance[k]; hyf_distance[k]:=hyf_distance[j]; hyf_distance[j]:=t;@/
> t:=hyf_num[k]; hyf_num[k]:=hyf_num[j]; hyf_num[j]:=t;@/
> t:=hyf_next[k]; hyf_next[k]:=hyf_next[j]; hyf_next[j]:=t;@/
> trie_op_hash[j]:=trie_op_hash[k]; trie_op_hash[k]:=k;
> end
>
> @ Before we forget how to initialize the data structures that have been
> mentioned so far, let's write down the code that gets them started.
>
> @<Initialize table entries...@>=
> for k:=-trie_op_size to trie_op_size do trie_op_hash[k]:=0;
> for k:=0 to 255 do trie_used[k]:=min_quarterword;
> trie_op_ptr:=0;
>
14986,14987c18373,18375
< The algorithms maintain the condition |trie_c[trie_r[z]]>trie_c[z]|
< whenever |z≠0| and |trie_r[z]≠0|; in other words, sibling nodes are
---
> The algorithms maintain the condition
> $$\hbox{|trie_c[trie_r[z]]>trie_c[z]|\qquad
> whenever |z<>0| and |trie_r[z]<>0|};$$ in other words, sibling nodes are
14992,14998c18380,18391
< @<Globals...@>=
< init @!trie_c:array[trie_pointer] of ascii_code; {characters to match}
< @t\hskip1em@>@!trie_o:array[trie_pointer] of quarterword;
< {operations to perform}
< @t\hskip1em@>@!trie_l:array[trie_pointer] of trie_pointer; {left subtrie links}
< @t\hskip1em@>@!trie_r:array[trie_pointer] of trie_pointer; {right subtrie links}
< @t\hskip1em@>@!trie_ptr:trie_pointer; {the number of nodes in the trie}
---
> @<Glob...@>=
> @!init @!trie_c:packed array[trie_pointer] of packed_ASCII_code;
> {characters to match}
> @t\hskip10pt@>@!trie_o:packed array[trie_pointer] of quarterword;
> {operations to perform}
> @t\hskip10pt@>@!trie_l:packed array[trie_pointer] of trie_pointer;
> {left subtrie links}
> @t\hskip10pt@>@!trie_r:packed array[trie_pointer] of trie_pointer;
> {right subtrie links}
> @t\hskip10pt@>@!trie_ptr:trie_pointer; {the number of nodes in the trie}
> @t\hskip10pt@>@!trie_hash:packed array[trie_pointer] of trie_pointer;
> {used to identify equivalent subtries}
15002c18395
< Experi\-ence shows that we can often reduce its size by recognizing common
---
> Experience shows that we can often reduce its size by recognizing common
15007,15011c18400
< @<Glob...@>=
< init @!trie_hash:array[trie_pointer] of trie_pointer;
< tini {to identify equivalent subtries}
<
< @ The function |trie_node(p)| returns |p| if |p| is distinct from other nodes
---
> The function |trie_node(p)| returns |p| if |p| is distinct from other nodes
15014a18404,18407
> Notice that we might make subtries equivalent even if they correspond to
> patterns for different languages, in which the trie ops might mean quite
> different things. That's perfectly all right.
>
15017c18410
< to a canonical form}
---
> to a canonical form}
15021,15032c18414,18425
< begin h←(trie_c[p]+1009*trie_o[p]+@|
< 2718*trie_l[p]+3142*trie_r[p]) mod trie_size;
< loop@+ begin q←trie_hash[h];
< if q=0 then
< begin trie_hash[h]←p; trie_node←p; return;
< end;
< if (trie_c[q]=trie_c[p])∧(trie_o[q]=trie_o[p])∧@|
< (trie_l[q]=trie_l[p])∧(trie_r[q]=trie_r[p]) then
< begin trie_node←q; return;
< end;
< if h>0 then decr(h)@+else h←trie_size;
< end;
---
> begin h:=abs(trie_c[p]+1009*trie_o[p]+@|
> 2718*trie_l[p]+3142*trie_r[p]) mod trie_size;
> loop@+ begin q:=trie_hash[h];
> if q=0 then
> begin trie_hash[h]:=p; trie_node:=p; return;
> end;
> if (trie_c[q]=trie_c[p])and(trie_o[q]=trie_o[p])and@|
> (trie_l[q]=trie_l[p])and(trie_r[q]=trie_r[p]) then
> begin trie_node:=q; return;
> end;
> if h>0 then decr(h)@+else h:=trie_size;
> end;
15038c18431
< zero and then saying `|trie_root←compress_trie(trie_root)|'.
---
> zero and then saying `|trie_root:=compress_trie(trie_root)|'.
15043,15059c18436,18440
< begin if p=0 then compress_trie←0
< else begin trie_l[p]←compress_trie(trie_l[p]);
< trie_r[p]←compress_trie(trie_r[p]);
< compress_trie←trie_node(p);
< end;
< end;
<
< @ Before we forget how to initialize the data structures that have been
< mentioned so far, let's write a procedure that does the initialization.
<
< @<Declare procedures for preprocessing hyph...@>=
< procedure init_pattern_memory; {gets ready to build a linked trie}
< var h:0..trie_op_hash_size; {an index into |trie_op_hash|}
< @!p:trie_pointer; {an index into |trie_hash|}
< begin for h←0 to trie_op_hash_size do trie_op_hash[h]←min_quarterword;
< trie_op_ptr←min_quarterword; trie_root←0; trie_c[0]←0; trie_ptr←0;
< for p←0 to trie_size do trie_hash[p]←0;
---
> begin if p=0 then compress_trie:=0
> else begin trie_l[p]:=compress_trie(trie_l[p]);
> trie_r[p]:=compress_trie(trie_r[p]);
> compress_trie:=trie_node(p);
> end;
15066c18447,18448
< |trie_ref[p]| will be nonzero if the linked trie node |p| is the oldest sibling
---
> |trie_ref[p]| will be nonzero only if the linked trie node |p| is the
> smallest character
15073a18456,18457
> To save time at the low end of the trie, we maintain array entries
> |trie_min[c]| pointing to the smallest hole that is greater than~|c|.
15083,15086c18467,18472
< init@!trie_taken:array[trie_pointer] of boolean; {does a family start here?}
< @t\hskip1em@>@!trie_max:trie_pointer; {largest location used in |trie|}
< @t\hskip1em@>@!trie_min:trie_pointer;
< {all locations |≤trie_min| are vacant in |trie|}
---
> @!init@!trie_taken:packed array[1..trie_size] of boolean;
> {does a family start here?}
> @t\hskip10pt@>@!trie_min:array[ASCII_code] of trie_pointer;
> {the first possible slot for each character}
> @t\hskip10pt@>@!trie_max:trie_pointer; {largest location used in |trie|}
> @t\hskip10pt@>@!trie_not_ready:boolean; {is the trie still in linked form?}
15089,15105c18475,18479
< @ Here is how these data structures are initialized.
<
< @<Declare procedures for preprocessing hyph...@>=
< procedure init_trie_memory; {gets ready to pack into |trie|}
< var p:trie_pointer; {index into |trie_ref|, |trie|, |trie_taken|}
< begin for p←0 to trie_ptr do trie_ref[p]←0;
< trie_max←128; trie_min←128; trie_link(0)←1; trie_taken[0]←false;
< for p←1 to 128 do
< begin trie_back(p)←p-1; trie_link(p)←p+1; trie_taken[p]←false;
< end;
< end;
<
< @ Each time \.{\\patterns} appears, it overrides any patterns that were
< entered earlier, so the arrays are not initialized until \TeX\ sees
< \.{\\patterns}. However, some of the global variables must be
< initialized when \.{INITEX} is loaded, in case the user never mentions
< any \.{\\patterns}.
---
> @ Each time \.{\\patterns} appears, it contributes further patterns to
> the future trie, which will be built only when hyphenation is attempted or
> when a format file is dumped. The boolean variable |trie_not_ready|
> will change to |false| when the trie is compressed; this will disable
> further patterns.
15108,15111c18482,18495
< trie_op_ptr←min_quarterword;@/
< trie_link(0)←0; trie_char(0)←0; trie_op(0)←0;
< for k←1 to 127 do trie[k]←trie[0];
< trie_max←127;
---
> trie_not_ready:=true; trie_root:=0; trie_c[0]:=si(0); trie_ptr:=0;
>
> @ Here is how the trie-compression data structures are initialized.
> If storage is tight, it would be possible to overlap |trie_op_hash|,
> |trie_op_lang|, and |trie_op_val| with |trie|, |trie_hash|, and |trie_taken|,
> because we finish with the former just before we need the latter.
>
> @<Get ready to compress the trie@>=
> @<Sort \(t)the hyphenation...@>;
> for p:=0 to trie_size do trie_hash[p]:=0;
> trie_root:=compress_trie(trie_root); {identify equivalent subtries}
> for p:=0 to trie_ptr do trie_ref[p]:=0;
> for p:=0 to 255 do trie_min[p]:=p+1;
> trie_link(0):=1; trie_max:=0
15126,15137c18510,18521
< @!c:ascii_code; {smallest character in the family}
< begin c←trie_c[p]; {we have |c≠0|}
< if c<trie_min then trie_min←c;
< z←trie_link(trie_min-1); {get the first conceivably good hole}
< loop@+ begin if z<c then goto not_found;
< h←z-c;@/
< @<Ensure that |trie_max≥h+128|@>;
< if trie_taken[h] then goto not_found;
< @<If all characters of the family fit relative to |h|, then
< |goto found|,\30\ otherwise |goto not_found|@>;
< not_found: z←trie_link(z); {move to the next hole}
< end;
---
> @!c:ASCII_code; {smallest character in the family}
> @!l,@!r:trie_pointer; {left and right neighbors}
> @!ll:1..256; {upper limit of |trie_min| updating}
> begin c:=so(trie_c[p]);
> z:=trie_min[c]; {get the first conceivably good hole}
> loop@+ begin h:=z-c;@/
> @<Ensure that |trie_max>=h+256|@>;
> if trie_taken[h] then goto not_found;
> @<If all characters of the family fit relative to |h|, then
> |goto found|,\30\ otherwise |goto not_found|@>;
> not_found: z:=trie_link(z); {move to the next hole}
> end;
15141,15151c18525,18536
< @ By making sure that |trie_max| is at least |h+128|, we can be sure that
< |trie_max>z|, since |h=z+c|. It follows that location |trie_max| will
< never be occupied in |trie|, and we will have |trie_max≥trie_link(z)|.
<
< @<Ensure that |trie_max≥h+128|@>=
< if trie_max<h+128 then
< begin if trie_size≤h+128 then overflow("pattern memory",trie_size);
< repeat incr(trie_max); trie_taken[trie_max]←false;
< trie_link(trie_max)←trie_max+1; trie_back(trie_max)←trie_max-1;
< until trie_max=h+128;
< end
---
> @ By making sure that |trie_max| is at least |h+256|, we can be sure that
> |trie_max>z|, since |h=z-c|. It follows that location |trie_max| will
> never be occupied in |trie|, and we will have |trie_max>=trie_link(z)|.
>
> @<Ensure that |trie_max>=h+256|@>=
> if trie_max<h+256 then
> begin if trie_size<=h+256 then overflow("pattern memory",trie_size);
> @:TeX capacity exceeded pattern memory}{\quad pattern memory@>
> repeat incr(trie_max); trie_taken[trie_max]:=false;
> trie_link(trie_max):=trie_max+1; trie_back(trie_max):=trie_max-1;
> until trie_max=h+256;
> end
15154c18539
< q←trie_r[p];
---
> q:=trie_r[p];
15156,15158c18541,18543
< begin if trie_link(h+trie_c[q])=0 then goto not_found;
< q←trie_r[q];
< end;
---
> begin if trie_link(h+so(trie_c[q]))=0 then goto not_found;
> q:=trie_r[q];
> end;
15162,15164c18547,18555
< trie_taken[h]←true; trie_ref[p]←h; q←p;
< repeat z←h+trie_c[q]; trie_back(trie_link(z))←trie_back(z);
< trie_link(trie_back(z))←trie_link(z); trie_link(z)←0; q←trie_r[q];
---
> trie_taken[h]:=true; trie_ref[p]:=h; q:=p;
> repeat z:=h+so(trie_c[q]); l:=trie_back(z); r:=trie_link(z);
> trie_back(r):=l; trie_link(l):=r; trie_link(z):=0;
> if l<256 then
> begin if z<256 then ll:=z @+else ll:=256;
> repeat trie_min[l]:=r; incr(l);
> until l=ll;
> end;
> q:=trie_r[q];
15173,15177c18564,18568
< begin repeat q←trie_l[p];
< if (q>0)∧(trie_ref[q]=0) then
< begin first_fit(q); trie_pack(q);
< end;
< p←trie_r[p];
---
> begin repeat q:=trie_l[p];
> if (q>0)and(trie_ref[q]=0) then
> begin first_fit(q); trie_pack(q);
> end;
> p:=trie_r[p];
15183,15188c18574,18575
< information. Null pointers in the linked trie will be replaced by the
< first untaken position |r| in |trie|, since this properly implements an
< ``empty'' family. The value of |r| is stored in |trie_ref[0]| just before
< the fixup process starts. Note that |trie_max| will always be at least as
< large as |r+127|, since it is always at least 128 more than each location
< that is taken.
---
> information. Null pointers in the linked trie will be represented by the
> value~0, which properly implements an ``empty'' family.
15191,15194c18578,18589
< r←0;
< while trie_taken[r] do incr(r);
< trie_ref[0]←r; {|r| will be used for null pointers}
< trie_fix(trie_root) {this fixes the non-holes in |trie|}
---
> h.rh:=0; h.b0:=min_quarterword; h.b1:=min_quarterword; {|trie_link:=0|,
> |trie_op:=min_quarterword|, |trie_char:=qi(0)|}
> if trie_root=0 then {no patterns were given}
> begin for r:=0 to 256 do trie[r]:=h;
> trie_max:=256;
> end
> else begin trie_fix(trie_root); {this fixes the non-holes in |trie|}
> r:=0; {now we will zero out all the holes}
> repeat s:=trie_link(r); trie[r]:=h; r:=s;
> until r>trie_max;
> end;
> trie_char(0):=qi("?"); {make |trie_char(c)<>c| for all |c|}
15205c18600
< @!c:ascii_code; {another one that need not be saved}
---
> @!c:ASCII_code; {another one that need not be saved}
15207,15213c18602,18607
< begin z←trie_ref[p];
< while p≠0 do
< begin q←trie_l[p]; c←trie_c[p];
< trie_link(z+c)←trie_ref[q]; trie_char(z+c)←c; trie_op(z+c)←trie_o[p];
< if q>0 then trie_fix(q);
< p←trie_r[p];
< end;
---
> begin z:=trie_ref[p];
> repeat q:=trie_l[p]; c:=so(trie_c[p]);
> trie_link(z+c):=trie_ref[q]; trie_char(z+c):=qi(c); trie_op(z+c):=trie_o[p];
> if q>0 then trie_fix(q);
> p:=trie_r[p];
> until p=0;
15216,15221c18610,18612
< @ Now let's put all these routines together. When \.{INITEX} has scanned
< the `\.{\\patterns}' control sequence, it calls on |new_patterns| to do
< the right thing. After |new_patterns| has acted, the compacted pattern data
< will appear in the array |trie[1..trie_max]|, and the associated numeric
< hyphenation data will appear in locations |[(min_quarterword+1)..trie_op_ptr]|
< of the arrays |hyf_distance|, |hyf_num|, |hyf_next|.
---
> @ Now let's go back to the easier problem, of building the linked
> trie. When \.{INITEX} has scanned the `\.{\\patterns}' control
> sequence, it calls on |new_patterns| to do the right thing.
15226c18617,18618
< var k,@!l:small_number; {indices into |hc| and |hyf|}
---
> var k,@!l:0..64; {indices into |hc| and |hyf|;
> not always in |small_number| range}
15231,15239c18623,18632
< @!c:ascii_code; {character being inserted}
< @!r,@!s:trie_pointer; {used to clean up the packed |trie|}
< @!h:two_halves; {template used to zero out |trie|'s holes}
< begin scan_left_brace; {a left brace must follow \.{\\patterns}}
< init_pattern_memory;@/
< @<Enter all of the patterns into a linked trie, until coming to a right
< brace; then skip an optional space@>;
< trie_root←compress_trie(trie_root); {compress the trie}
< @<Pack the trie@>;
---
> @!c:ASCII_code; {character being inserted}
> begin if trie_not_ready then
> begin set_cur_lang; scan_left_brace; {a left brace must follow \.{\\patterns}}
> @<Enter all of the patterns into a linked trie, until coming to a right
> brace@>;
> end
> else begin print_err("Too late for "); print_esc("patterns");
> help1("All patterns must be given before typesetting begins.");
> error; link(garbage):=scan_toks(false,false); flush_list(def_ref);
> end;
15247,15258c18640,18649
< k←0; hyf[0]←0; digit_sensed←false;
< loop@+ begin get_nc_token;
< case cur_cmd of
< letter,other_char:@<Append a new letter or a hyphen level@>;
< spacer,right_brace: begin if k>0 then
< @<Insert a new pattern into the linked trie@>;
< if cur_cmd=right_brace then
< begin @<Scan an optional space@>; goto done;
< end;
< k←0; hyf[0]←0; digit_sensed←false;
< end;
< othercases begin print_nl("! Bad \patterns");
---
> k:=0; hyf[0]:=0; digit_sensed:=false;
> loop@+ begin get_x_token;
> case cur_cmd of
> letter,other_char:@<Append a new letter or a hyphen level@>;
> spacer,right_brace: begin if k>0 then
> @<Insert a new pattern into the linked trie@>;
> if cur_cmd=right_brace then goto done;
> k:=0; hyf[0]:=0; digit_sensed:=false;
> end;
> othercases begin print_err("Bad "); print_esc("patterns");
15260,15263c18651,18654
< help1("(See Appendix H.)"); error; pass_block(1); goto done;
< end
< endcases;
< end;
---
> help1("(See Appendix H.)"); error;
> end
> endcases;
> end;
15267,15272c18658,18662
< if digit_sensed ∨(cur_chr<"0")∨(cur_chr>"9") then
< begin if cur_chr="." then cur_chr←127 {change edge-of-word delimiter
< to |@'177|}
< else begin cur_chr←lc_code(cur_chr);
< if cur_chr=0 then
< begin print_nl("! Nonletter");
---
> if digit_sensed or(cur_chr<"0")or(cur_chr>"9") then
> begin if cur_chr="." then cur_chr:=0 {edge-of-word delimiter}
> else begin cur_chr:=lc_code(cur_chr);
> if cur_chr=0 then
> begin print_err("Nonletter");
15274,15286c18664,18677
< help1("(See Appendix H.)"); error; cur_chr←127;
< end;
< end;
< if k<63 then
< begin incr(k); hc[k]←cur_chr; hyf[k]←0; digit_sensed←false;
< end;
< end
< else begin hyf[k]←cur_chr-"0"; digit_sensed←true;
< end
<
< @ When the following code comes into play, the pattern $p↓1\ldotsm p↓k$
< appears in |hc[1..k]|, and the corresponding sequence of numbers $n↓0\ldotsm
< n↓k$ appears in |hyf[0..k]|.
---
> help1("(See Appendix H.)"); error;
> end;
> end;
> if k<63 then
> begin incr(k); hc[k]:=cur_chr; hyf[k]:=0; digit_sensed:=false;
> end;
> end
> else if k<63 then
> begin hyf[k]:=cur_chr-"0"; digit_sensed:=true;
> end
>
> @ When the following code comes into play, the pattern $p_1\ldots p_k$
> appears in |hc[1..k]|, and the corresponding sequence of numbers $n_0\ldots
> n_k$ appears in |hyf[0..k]|.
15289,15302c18680,18693
< begin @<Compute the trie op code, |v|, and set |l←0|@>;
< q←0;
< while l<k do
< begin incr(l); c←hc[l]; p←trie_l[q]; first_child←true;
< while (p>0)∧(c>trie_c[p]) do
< begin q←p; p←trie_r[q]; first_child←false;
< end;
< if (p=0)∨(c<trie_c[p]) then
< @<Insert a new trie node between |q| and |p|, and
< make |p| point to it@>;
< q←p; {now node |q| represents $p↓1\ldotsm p↓l$}
< end;
< if trie_o[q]≠min_quarterword then
< begin print_nl("! Duplicate pattern");
---
> begin @<Compute the trie op code, |v|, and set |l:=0|@>;
> q:=0; hc[0]:=cur_lang;
> while l<=k do
> begin c:=hc[l]; incr(l); p:=trie_l[q]; first_child:=true;
> while (p>0)and(c>so(trie_c[p])) do
> begin q:=p; p:=trie_r[q]; first_child:=false;
> end;
> if (p=0)or(c<so(trie_c[p])) then
> @<Insert a new trie node between |q| and |p|, and
> make |p| point to it@>;
> q:=p; {now node |q| represents $p_1\ldots p_{l-1}$}
> end;
> if trie_o[q]<>min_quarterword then
> begin print_err("Duplicate pattern");
15304,15306c18695,18697
< help1("(See Appendix H.)"); error;
< end;
< trie_o[q]←v;
---
> help1("(See Appendix H.)"); error;
> end;
> trie_o[q]:=v;
15311,15313c18702,18705
< incr(trie_ptr); trie_r[trie_ptr]←p; p←trie_ptr; trie_l[p]←0;
< if first_child then trie_l[q]←p@+else trie_r[q]←p;
< trie_c[p]←c; trie_o[p]←min_quarterword;
---
> @:TeX capacity exceeded pattern memory}{\quad pattern memory@>
> incr(trie_ptr); trie_r[trie_ptr]:=p; p:=trie_ptr; trie_l[p]:=0;
> if first_child then trie_l[q]:=p@+else trie_r[q]:=p;
> trie_c[p]:=si(c); trie_o[p]:=min_quarterword;
15317,15320c18709,18714
< l←k; v←min_quarterword;
< loop@+ begin if hyf[l]≠0 then v←new_trie_op(k-l,hyf[l],v);
< if l>0 then decr(l)@+else goto done1;
< end;
---
> if hc[1]=0 then hyf[0]:=0;
> if hc[k]=0 then hyf[k]:=0;
> l:=k; v:=min_quarterword;
> loop@+ begin if hyf[l]<>0 then v:=new_trie_op(k-l,hyf[l],v);
> if l>0 then decr(l)@+else goto done1;
> end;
15323,15324c18717,18720
< @ The following packing routine is rigged so that the root of the linked
< tree gets mapped into location 0 of |trie|, as required by the hyphenation
---
> @ Finally we put everything together: Here is how the trie gets to its
> final, efficient form.
> The following packing routine is rigged so that the root of the linked
> tree gets mapped into location 1 of |trie|, as required by the hyphenation
15326c18722
< ``take'' location@@0.
---
> ``take'' location~1.
15328,15332c18724,18733
< @<Pack the trie@>=
< init_trie_memory;
< if trie_root≠0 then
< begin first_fit(trie_root); trie_pack(trie_root);
< end;
---
> @<Declare procedures for preprocessing hyphenation patterns@>=
> procedure init_trie;
> var @!p:trie_pointer; {pointer for initialization}
> @!j,@!k,@!t:integer; {all-purpose registers for initialization}
> @!r,@!s:trie_pointer; {used to clean up the packed |trie|}
> @!h:two_halves; {template used to zero out |trie|'s holes}
> begin @<Get ready to compress the trie@>;
> if trie_root<>0 then
> begin first_fit(trie_root); trie_pack(trie_root);
> end;
15334,15339c18735,18738
< r←0; {finally, we will zero out the holes}
< h.rh←0; h.b0←min_quarterword; h.b1←0; {|trie_link←0|,
< |trie_op←min_quarterword|, |trie_op←0|}
< repeat s←trie_link(r); trie[r]←h; r←s;
< until r>trie_max
< @* \[43] Breaking vertical lists into pages.
---
> trie_not_ready:=false;
> end;
>
> @* \[44] Breaking vertical lists into pages.
15355a18755
> @^data structure assumptions@>
15360,15373c18760,18773
< begin prev_p←temp_head; link(temp_head)←p;
< while p≠null do
< case type(p) of
< hlist_node,vlist_node,rule_node:@<Insert glue for |split_top_skip|
< and set@@|p←null|@>;
< whatsit_node,mark_node,ins_node: begin prev_p←p; p←link(prev_p);
< end;
< glue_node,kern_node,penalty_node: begin q←p; p←link(q); link(q)←null;
< link(prev_p)←p; flush_node_list(q);
< end;
< othercases confusion("pruning")
< @:confusion pruning}{\quad pruning@>
< endcases;
< prune_page_top←link(temp_head);
---
> begin prev_p:=temp_head; link(temp_head):=p;
> while p<>null do
> case type(p) of
> hlist_node,vlist_node,rule_node:@<Insert glue for |split_top_skip|
> and set~|p:=null|@>;
> whatsit_node,mark_node,ins_node: begin prev_p:=p; p:=link(prev_p);
> end;
> glue_node,kern_node,penalty_node: begin q:=p; p:=link(q); link(q):=null;
> link(prev_p):=p; flush_node_list(q);
> end;
> othercases confusion("pruning")
> @:this can't happen pruning}{\quad pruning@>
> endcases;
> prune_page_top:=link(temp_head);
15377,15381c18777,18781
< begin q←new_skip_param(split_top_skip_code); link(prev_p)←q; link(q)←p;
< {now |temp_ptr=glue_ptr(q)|}
< if width(temp_ptr)>height(p) then width(temp_ptr)←width(temp_ptr)-height(p)
< else width(temp_ptr)←0;
< p←null;
---
> begin q:=new_skip_param(split_top_skip_code); link(prev_p):=q; link(q):=p;
> {now |temp_ptr=glue_ptr(q)|}
> if width(temp_ptr)>height(p) then width(temp_ptr):=width(temp_ptr)-height(p)
> else width(temp_ptr):=0;
> p:=null;
15385,15386c18785,18786
< so as to obtain a box of height |h|, taking account of the |split_max_depth|
< parameter. A pointer to the beginning of the vertical list is given,
---
> so as to obtain a box of height~|h|, with maximum depth~|d|.
> A pointer to the beginning of the vertical list is given,
15398c18798
< @d set_height_zero(#)==active_height[#]←0 {initialize the height to zero}
---
> @d set_height_zero(#)==active_height[#]:=0 {initialize the height to zero}
15402,15403c18802,18803
< @p function vert_break(@!p:pointer; @!h:scaled):pointer;
< {finds optimum page break}
---
> @p function vert_break(@!p:pointer; @!h,@!d:scaled):pointer;
> {finds optimum page break}
15406c18806
< whether |p| is a legal breakpoint}
---
> whether |p| is a legal breakpoint}
15410,15411c18810,18811
< @!least_badness:integer; {the smallest badness found so far}
< @!best_place:pointer; {the most recent break that leads to |least_badness|}
---
> @!least_cost:integer; {the smallest badness plus penalties found so far}
> @!best_place:pointer; {the most recent break that leads to |least_cost|}
15414,15421c18814,18821
< begin prev_p←p; {an initial glue node is not a legal breakpoint}
< least_badness←awful_bad; do_all_six(set_height_zero); prev_dp←0;
< loop@+ begin @<If node |p| is a legal breakpoint, check if this break is
< the best known, and |goto done| if |p| is null or
< if the page-so-far is already too full to accept more stuff@>;
< prev_p←p; p←link(prev_p);
< end;
< done: vert_break←best_place;
---
> begin prev_p:=p; {an initial glue node is not a legal breakpoint}
> least_cost:=awful_bad; do_all_six(set_height_zero); prev_dp:=0;
> loop@+ begin @<If node |p| is a legal breakpoint, check if this break is
> the best known, and |goto done| if |p| is null or
> if the page-so-far is already too full to accept more stuff@>;
> prev_p:=p; p:=link(prev_p);
> end;
> done: vert_break:=best_place;
15430c18830
< shrinking}
---
> shrinking}
15432,15434c18832,18834
< @ A subtle point to be noted here is that |split_max_depth| might be negative,
< so |cur_height| and |prev_dp| might need to be corrected even after a
< glue or kern node.
---
> @ A subtle point to be noted here is that the maximum depth~|d| might be
> negative, so |cur_height| and |prev_dp| might need to be corrected even
> after a glue or kern node.
15437,15441c18837,18841
< if p=null then pi←eject_penalty
< else @<Use node |p| to update the current height and depth measurements;
< if this node is not a legal breakpoint, |goto not_found|
< or |update_heights|,
< otherwise set |pi| to the associated penalty at the break@>;
---
> if p=null then pi:=eject_penalty
> else @<Use node |p| to update the current height and depth measurements;
> if this node is not a legal breakpoint, |goto not_found|
> or |update_heights|,
> otherwise set |pi| to the associated penalty at the break@>;
15443,15444c18843,18844
< if |p| is a forced break or if the page-so-far is already too full@>;
< if (type(p)<glue_node)∨(type(p)>kern_node) then goto not_found;
---
> if |p| is a forced break or if the page-so-far is already too full@>;
> if (type(p)<glue_node)or(type(p)>kern_node) then goto not_found;
15446,15450c18846,18850
< respect to a glue or kern node |p|@>;
< not_found: if prev_dp>split_max_depth then
< begin cur_height←cur_height+prev_dp-split_max_depth;
< prev_dp←split_max_depth;
< end;
---
> respect to a glue or kern node~|p|@>;
> not_found: if prev_dp>d then
> begin cur_height:=cur_height+prev_dp-d;
> prev_dp:=d;
> end;
15455,15457c18855,18857
< cur_height←cur_height+prev_dp+height(p); prev_dp←depth(p);
< goto not_found;
< end;
---
> cur_height:=cur_height+prev_dp+height(p); prev_dp:=depth(p);
> goto not_found;
> end;
15459,15465c18859,18865
< glue_node: if precedes_break(prev_p) then pi←0
< else goto update_heights;
< kern_node: begin if link(p)=null then t←penalty_node
< else t←type(link(p));
< if t=glue_node then pi←0@+else goto update_heights;
< end;
< penalty_node: pi←penalty(p);
---
> glue_node: if precedes_break(prev_p) then pi:=0
> else goto update_heights;
> kern_node: begin if link(p)=null then t:=penalty_node
> else t:=type(link(p));
> if t=glue_node then pi:=0@+else goto update_heights;
> end;
> penalty_node: pi:=penalty(p);
15468c18868
< @:confusion vertbreak}{\quad vertbreak@>
---
> @:this can't happen vertbreak}{\quad vertbreak@>
15471c18871,18873
< @ @<Check if node |p| is a new champion breakpoint; then \(go)...@>=
---
> @ @d deplorable==100000 {more than |inf_bad|, but less than |awful_bad|}
>
> @<Check if node |p| is a new champion breakpoint; then \(go)...@>=
15473,15482c18875,18886
< begin @<Compute the badness, |b|, using |awful_bad|
< if the box is too full@>;
< if b≤inf_bad then
< if pi≤eject_penalty then b←pi@+else b←b+pi;
< if b≤least_badness then
< begin best_place←p; least_badness←b;
< best_height_plus_depth←cur_height+prev_dp;
< end;
< if (b=awful_bad)∨(pi≤eject_penalty) then goto done;
< end
---
> begin @<Compute the badness, |b|, using |awful_bad|
> if the box is too full@>;
> if b<awful_bad then
> if pi<=eject_penalty then b:=pi
> else if b<inf_bad then b:=b+pi
> else b:=deplorable;
> if b<=least_cost then
> begin best_place:=p; least_cost:=b;
> best_height_plus_depth:=cur_height+prev_dp;
> end;
> if (b=awful_bad)or(pi<=eject_penalty) then goto done;
> end
15486,15490c18890,18894
< if (active_height[3]≠0) or (active_height[4]≠0) or
< (active_height[5]≠0) then b←0
< else b←badness(h-cur_height,active_height[2])
< else if cur_height-h>active_height[6] then b←awful_bad
< else b←badness(cur_height-h,active_height[6])
---
> if (active_height[3]<>0) or (active_height[4]<>0) or
> (active_height[5]<>0) then b:=0
> else b:=badness(h-cur_height,active_height[2])
> else if cur_height-h>active_height[6] then b:=awful_bad
> else b:=badness(cur_height-h,active_height[6])
15497,15504c18901,18908
< if type(p)=kern_node then q←p
< else begin q←glue_ptr(p);
< active_height[2+stretch_order(q)]←@|
< active_height[2+stretch_order(q)]+stretch(q);@/
< active_height[6]←active_height[6]+shrink(q);
< if (shrink_order(q)≠normal)∧(shrink(q)≠0) then
< begin@t@>@;@/
< print_nl("! Infinite glue shrinkage found in box being split");@/
---
> if type(p)=kern_node then q:=p
> else begin q:=glue_ptr(p);
> active_height[2+stretch_order(q)]:=@|
> active_height[2+stretch_order(q)]+stretch(q);@/
> active_height[6]:=active_height[6]+shrink(q);
> if (shrink_order(q)<>normal)and(shrink(q)<>0) then
> begin@t@>@;@/
> print_err("Infinite glue shrinkage found in box being split");@/
15506,15514c18910,18918
< help4("The box you are \vsplitting contains some infinitely")@/
< ("shrinkable glue, e.g., `\vss' or `\vskip 0pt minus 1fil'.")@/
< ("Such glue doesn't belong there; but you can safely proceed,")@/
< ("since the offensive shrinkability has been made finite.");
< error; r←new_spec(q); shrink_order(r)←normal; delete_glue_ref(q);
< glue_ptr(p)←r;
< end;
< end;
< cur_height←cur_height+prev_dp+width(q); prev_dp←0
---
> help4("The box you are \vsplitting contains some infinitely")@/
> ("shrinkable glue, e.g., `\vss' or `\vskip 0pt minus 1fil'.")@/
> ("Such glue doesn't belong there; but you can safely proceed,")@/
> ("since the offensive shrinkability has been made finite.");
> error; r:=new_spec(q); shrink_order(r):=normal; delete_glue_ref(q);
> glue_ptr(p):=r; q:=r;
> end;
> end;
> cur_height:=cur_height+prev_dp+width(q); prev_dp:=0
15521c18925
< returns a box for a page of height@@|h|. The remainder of the vlist, if
---
> returns a box for a page of height~|h|. The remainder of the vlist, if
15527,15529c18931,18933
< The original box becomes ``absent'' if and only if it has been entirely
< extracted. The extracted box is ``absent'' if and only if the original
< box was absent (or if it was, erroneously, an hlist box).
---
> The original box becomes ``void'' if and only if it has been entirely
> extracted. The extracted box is ``void'' if and only if the original
> box was void (or if it was, erroneously, an hlist box).
15531,15532c18935,18936
< @p function vsplit(@!n:quarterword; @!h:scaled):pointer;
< {extracts a page of height |h| from box |n|}
---
> @p function vsplit(@!n:eight_bits; @!h:scaled):pointer;
> {extracts a page of height |h| from box |n|}
15537,15543c18941,18947
< begin v←box(n);
< if split_first_mark≠null then
< begin delete_token_ref(split_first_mark); split_first_mark←null;
< delete_token_ref(split_bot_mark); split_bot_mark←null;
< end;
< @<Dispense with trivial cases of absent or bad boxes@>;
< q←vert_break(list_ptr(v),h);
---
> begin v:=box(n);
> if split_first_mark<>null then
> begin delete_token_ref(split_first_mark); split_first_mark:=null;
> delete_token_ref(split_bot_mark); split_bot_mark:=null;
> end;
> @<Dispense with trivial cases of void or bad boxes@>;
> q:=vert_break(list_ptr(v),h,split_max_depth);
15545,15549c18949,18953
< link to |null| at the break@>;
< q←prune_page_top(q); p←list_ptr(v); free_node(v,box_node_size);
< if q=null then box(n)←null {the |eq_level| of the box stays the same}
< else box(n)←vpack(q,natural);
< vsplit←vpackage(p,h,exactly,split_max_depth);
---
> link to |null| at the break@>;
> q:=prune_page_top(q); p:=list_ptr(v); free_node(v,box_node_size);
> if q=null then box(n):=null {the |eq_level| of the box stays the same}
> else box(n):=vpack(q,natural);
> vsplit:=vpackage(p,h,exactly,split_max_depth);
15552c18956
< @ @<Dispense with trivial cases of absent or bad boxes@>=
---
> @ @<Dispense with trivial cases of void or bad boxes@>=
15554,15559c18958,18962
< begin vsplit←null; return;
< end;
< if type(v)≠vlist_node then
< begin help2("The box you are trying to split is an \hbox.")@/
< ("I can't split such boxes, so I'll leave it alone.");
< print_nl("! \vsplit needs a \vbox"); error; vsplit←null; return;
---
> begin vsplit:=null; return;
> end;
> if type(v)<>vlist_node then
> begin print_err(""); print_esc("vsplit"); print(" needs a ");
> print_esc("vbox");
15561c18964,18967
< end
---
> help2("The box you are trying to split is an \hbox.")@/
> ("I can't split such a box, so I'll leave it alone.");
> error; vsplit:=null; return;
> end
15567,15568c18973,18974
< p←list_ptr(v);
< if p=q then list_ptr(v)←null
---
> p:=list_ptr(v);
> if p=q then list_ptr(v):=null
15570,15584c18976,18990
< if split_first_mark=null then
< begin split_first_mark←mark_ptr(p);
< split_bot_mark←split_first_mark;
< token_ref_count(split_first_mark)←@|
< token_ref_count(split_first_mark)+2;
< end
< else begin delete_token_ref(split_bot_mark);
< split_bot_mark←mark_ptr(p);
< add_token_ref(split_bot_mark);
< end;
< if link(p)=q then
< begin link(p)←null; goto done;
< end;
< p←link(p);
< end;
---
> if split_first_mark=null then
> begin split_first_mark:=mark_ptr(p);
> split_bot_mark:=split_first_mark;
> token_ref_count(split_first_mark):=@|
> token_ref_count(split_first_mark)+2;
> end
> else begin delete_token_ref(split_bot_mark);
> split_bot_mark:=mark_ptr(p);
> add_token_ref(split_bot_mark);
> end;
> if link(p)=q then
> begin link(p):=null; goto done;
> end;
> p:=link(p);
> end;
15586c18992,18993
< @* \[44] The page builder.
---
>
> @* \[45] The page builder.
15589,15590c18996,18997
< the calculations are done ``on line'' as new items are placed on the list.
< The main complication in this process is that insertions have to be put
---
> the calculations are done ``on line'' as new items come in.
> The main complication in this process is that insertions must be put
15608,15609c19015,19016
< the current |vsize| and |max_depth| are squirreled away into |page_size|
< and |page_depth_max|; the latter values will be used until the page has
---
> the current |vsize| and |max_depth| are squirreled away into |page_goal|
> and |page_max_depth|; the latter values will be used until the page has
15613c19020
< Although |page_size| starts out equal to |vsize|, it is decreased by the
---
> Although |page_goal| starts out equal to |vsize|, it is decreased by the
15619,15623c19026,19031
< The variables |best_page_break| and |least_page_badness| correspond to the
< variables |best_place| and |least_badness| in the |vert_break| routine
< that we have already studied; i.e., they record the location and value of
< the best place currently known for breaking the current page. The value of
< |page_size| at the time of the best break is stored in |best_size|.
---
> The global variables |best_page_break| and |least_page_cost| correspond
> respectively to the local variables |best_place| and |least_cost| in the
> |vert_break| routine that we have already studied; i.e., they record the
> location and value of the best place currently known for breaking the
> current page. The value of |page_goal| at the time of the best break is
> stored in |best_size|.
15626c19034
< {|page_contents| when an insert node has been contributed, but no boxes}
---
> {|page_contents| when an insert node has been contributed, but no boxes}
15632,15633c19040
< @!page_size:scaled; {desired height of information on page being built}
< @!page_depth_max:scaled; {maximum box depth on page being built}
---
> @!page_max_depth:scaled; {maximum box depth on page being built}
15635,15636c19042,19043
< @!least_page_badness:integer; {the score for this currently best page}
< @!best_size:scaled; {its |page_size|}
---
> @!least_page_cost:integer; {the score for this currently best page}
> @!best_size:scaled; {its |page_goal|}
15640,15642c19047,19049
< That is, the first element of the list is node |r@t$↓1$@>=link(page_ins_head)|;
< node $r↓j$ is followed by |r@t$↓{j+1}$@>=link(r@t$↓j$@>)|; and if there are
< |n| items we have |r@t$↓{n+1}$@>=page_ins_head|. The |subtype| field of
---
> That is, the first element of the list is node |r@t$_1$@>=link(page_ins_head)|;
> node $r_j$ is followed by |r@t$_{j+1}$@>=link(r@t$_j$@>)|; and if there are
> |n| items we have |r@t$_{n+1}$@>=page_ins_head|. The |subtype| field of
15653c19060
< If |type(r)=inserting|, then |width(r)| contains the total of the
---
> If |type(r)=inserting|, then |height(r)| contains the total of the
15659c19066
< insertion node that was tentatively split, and |width(r)| includes the
---
> insertion node that was tentatively split, and |height(r)| includes also the
15669a19077,19080
> The data structure definitions here use the fact that the |@!height| field
> appears in the fourth word of a box node.
> @^data structure assumptions@>
>
15673,15675c19084,19086
< @d broken_ptr(#)==link(#+3)
< {an insertion for this class will break here if anywhere}
< @d broken_ins(#)==info(#+3) {this insertion might break at |broken_ptr|}
---
> @d broken_ptr(#)==link(#+1)
> {an insertion for this class will break here if anywhere}
> @d broken_ins(#)==info(#+1) {this insertion might break at |broken_ptr|}
15680,15681c19091,19092
< subtype(page_ins_head)←qi(255);
< type(page_ins_head)←split_up; link(page_ins_head)←page_ins_head;
---
> subtype(page_ins_head):=qi(255);
> type(page_ins_head):=split_up; link(page_ins_head):=page_ins_head;
15683c19094
< @ An array |page_so_far| records the heights and depths of everything that
---
> @ An array |page_so_far| records the heights and depths of everything
15685,15686c19096,19099
< similar arrays already considered in |line_break| and |vert_break|. The
< value of |page_so_far[1]| is also called |cur_page_height|. The stretch
---
> similar arrays already considered in |line_break| and |vert_break|; and it
> also contains |page_goal| and |page_depth|, since these values are
> all accessible to the user via |set_page_dimen| commands. The
> value of |page_so_far[1]| is also called |page_total|. The stretch
15689c19102,19142
< corrections are not, since they have been subtracted from |page_size|.
---
> corrections are not, since they have been subtracted from |page_goal|.
>
> The variable |page_depth| records the depth of the current page; it has been
> adjusted so that it is at most |page_max_depth|. The variable
> |last_glue| points to the glue specification of the most recent node
> contributed from the contribution list, if this was a glue node; otherwise
> |last_glue=max_halfword|. (If the contribution list is nonempty,
> however, the value of |last_glue| is not necessarily accurate.)
> The variables |last_penalty| and |last_kern| are similar. And
> finally, |insert_penalties| holds the sum of the penalties associated with
> all split and floating insertions.
>
> @d page_goal==page_so_far[0] {desired height of information on page being built}
> @d page_total==page_so_far[1] {height of the current page}
> @d page_shrink==page_so_far[6] {shrinkability of the current page}
> @d page_depth==page_so_far[7] {depth of the current page}
>
> @<Glob...@>=
> @!page_so_far:array [0..7] of scaled; {height and glue of the current page}
> @!last_glue:pointer; {used to implement \.{\\lastskip}}
> @!last_penalty:integer; {used to implement \.{\\lastpenalty}}
> @!last_kern:scaled; {used to implement \.{\\lastkern}}
> @!insert_penalties:integer; {sum of the penalties for held-over insertions}
>
> @ @<Put each...@>=
> primitive("pagegoal",set_page_dimen,0);
> @!@:page_goal_}{\.{\\pagegoal} primitive@>
> primitive("pagetotal",set_page_dimen,1);
> @!@:page_total_}{\.{\\pagetotal} primitive@>
> primitive("pagestretch",set_page_dimen,2);
> @!@:page_stretch_}{\.{\\pagestretch} primitive@>
> primitive("pagefilstretch",set_page_dimen,3);
> @!@:page_fil_stretch_}{\.{\\pagefilstretch} primitive@>
> primitive("pagefillstretch",set_page_dimen,4);
> @!@:page_fill_stretch_}{\.{\\pagefillstretch} primitive@>
> primitive("pagefilllstretch",set_page_dimen,5);
> @!@:page_filll_stretch_}{\.{\\pagefilllstretch} primitive@>
> primitive("pageshrink",set_page_dimen,6);
> @!@:page_shrink_}{\.{\\pageshrink} primitive@>
> primitive("pagedepth",set_page_dimen,7);
> @!@:page_depth_}{\.{\\pagedepth} primitive@>
15691,15706c19144,19154
< Another variable, |cur_page_depth|, records the depth of the current page,
< adjusted to be at most |page_depth_max|. The variable |last_page_glue|
< points to the glue specification of the most recent node contributed from
< the contribution list, if this was a glue node; otherwise
< |last_page_glue=max_halfword|. (If the contribution list is nonempty,
< however, the value of |last_page_glue| is not necessarily accurate.) And
< finally, |ins_penalties| holds the sum of the penalties associated with
< all split insertions.
<
< @d cur_page_height==page_so_far[1] {height of the current page}
<
< @<Glob...@>=
< @!page_so_far:array [1..6] of scaled; {height and glue of the current page}
< @!cur_page_depth:scaled; {depth of the current page}
< @!last_page_glue:pointer; {used to implement \.{\\lastskip}}
< @!ins_penalties:integer; {sum of the penalties for split insertions}
---
> @ @<Cases of |print_cmd_chr|...@>=
> set_page_dimen: case chr_code of
> 0: print_esc("pagegoal");
> 1: print_esc("pagetotal");
> 2: print_esc("pagestretch");
> 3: print_esc("pagefilstretch");
> 4: print_esc("pagefillstretch");
> 5: print_esc("pagefilllstretch");
> 6: print_esc("pageshrink");
> othercases print_esc("pagedepth")
> endcases;
15709,15710c19157,19158
< @d print_plus(#)==if page_so_far[#]≠0 then
< begin print(" plus "); print_scaled(page_so_far[#]); print_plus_end
---
> @d print_plus(#)==if page_so_far[#]<>0 then
> begin print(" plus "); print_scaled(page_so_far[#]); print_plus_end
15712,15738c19160,19199
< @<Show the status of the current page@>=
< show_box(link(page_head));
< if page_contents>empty then
< begin print_nl("total height "); print_scaled(page_so_far[1]);
< print_plus(2)("");
< print_plus(3)("fil");
< print_plus(4)("fill");
< print_plus(5)("filll");
< if page_so_far[6]≠0 then
< begin print(" minus "); print_scaled(page_so_far[6]);
< end;
< print_nl(" goal height"); print_scaled(page_size);
< r←link(page_ins_head);
< while r≠page_ins_head do
< begin print_ln; print_esc("insert"); a←qo(subtype(r));
< print_int(a); print(" adds ");
< a←x_over_n(width(r),1000)*count(a); print_scaled(a);
< if type(r)=split_up then
< begin q←page_head; a←0;
< repeat q←link(q);
< if (type(q)=ins_node)∧(subtype(q)=subtype(r)) then incr(a);
< until q=broken_ins(r);
< print(", #"); print_int(a); print(" might split");
< end;
< r←link(r);
< end;
< end
---
> @p procedure print_totals;
> begin print_scaled(page_total);
> print_plus(2)("");
> print_plus(3)("fil");
> print_plus(4)("fill");
> print_plus(5)("filll");
> if page_shrink<>0 then
> begin print(" minus "); print_scaled(page_shrink);
> end;
> end;
>
> @ @<Show the status of the current page@>=
> if page_head<>page_tail then
> begin print_nl("### current page:");
> if output_active then print(" (held over for next output)");
> @.held over for next output@>
> show_box(link(page_head));
> if page_contents>empty then
> begin print_nl("total height "); print_totals;
> @:total_height}{\.{total height}@>
> print_nl(" goal height "); print_scaled(page_goal);
> @.goal height@>
> r:=link(page_ins_head);
> while r<>page_ins_head do
> begin print_ln; print_esc("insert"); t:=qo(subtype(r));
> print_int(t); print(" adds ");
> if count(t)=1000 then t:=height(r)
> else t:=x_over_n(height(r),1000)*count(t);
> print_scaled(t);
> if type(r)=split_up then
> begin q:=page_head; t:=0;
> repeat q:=link(q);
> if (type(q)=ins_node)and(subtype(q)=subtype(r)) then incr(t);
> until q=broken_ins(r);
> print(", #"); print_int(t); print(" might split");
> end;
> r:=link(r);
> end;
> end;
> end
15743c19204
< @d set_page_so_far_zero(#)==page_so_far[#]←0
---
> @d set_page_so_far_zero(#)==page_so_far[#]:=0
15746,15749c19207,19217
< begin page_contents←s;
< page_size←vsize; page_depth_max←max_depth;
< cur_page_depth←0; do_all_six(set_page_so_far_zero);
< least_page_badness←awful_bad;
---
> begin page_contents:=s;
> page_goal:=vsize; page_max_depth:=max_depth;
> page_depth:=0; do_all_six(set_page_so_far_zero);
> least_page_cost:=awful_bad;
> @!stat if tracing_pages>0 then
> begin begin_diagnostic;
> print_nl("%% goal height="); print_scaled(page_goal);
> @.goal height@>
> print(", max depth="); print_scaled(page_max_depth);
> end_diagnostic(false);
> end;@;@+tats@;@/
15759c19227,19228
< generated by other parts of \TeX\ but not yet seen by the page builder.
---
> generated by other parts of \TeX\ but have not yet been
> seen by the page builder.
15776c19245
< type(page_head)←glue_node; subtype(page_head)←normal;
---
> type(page_head):=glue_node; subtype(page_head):=normal;
15785c19254
< output_active←false;
---
> output_active:=false; insert_penalties:=0;
15792,15794c19261,19292
< page_contents←empty; page_tail←page_head; link(page_head)←null;@/
< last_page_glue←max_halfword; ins_penalties←0;
< cur_page_depth←0; page_depth_max←0;
---
> page_contents:=empty; page_tail:=page_head; link(page_head):=null;@/
> last_glue:=max_halfword; last_penalty:=0; last_kern:=0;
> page_depth:=0; page_max_depth:=0
>
> @ At certain times box 255 is supposed to be void (i.e., |null|),
> or an insertion box is supposed to be ready to accept a vertical list.
> If not, an error message is printed, and the following subroutine
> flushes the unwanted contents, reporting them to the user.
>
> @p procedure box_error(@!n:eight_bits);
> begin error; begin_diagnostic;
> print_nl("The following box has been deleted:");
> @.The following...deleted@>
> show_box(box(n)); end_diagnostic(true);
> flush_node_list(box(n)); box(n):=null;
> end;
>
> @ The following procedure guarantees that a given box register
> does not contain an \.{\\hbox}.
>
> @p procedure ensure_vbox(@!n:eight_bits);
> var p:pointer; {the box register contents}
> begin p:=box(n);
> if p<>null then if type(p)=hlist_node then
> begin print_err("Insertions can only be added to a vbox");
> @.Insertions can only...@>
> help3("Tut tut: You're trying to \insert into a")@/
> ("\box register that now contains an \hbox.")@/
> ("Proceed, and I'll discard its present contents.");
> box_error(n);
> end;
> end;
15804c19302,19303
< @p procedure build_page; {append contributions to the current page}
---
> @p @t\4@>@<Declare the procedure called |fire_up|@>@;@/
> procedure build_page; {append contributions to the current page}
15807,15809c19306,19307
< @!q,@!r,@!s:pointer; {nodes being examined}
< @!prev_p:pointer; {precedessor of |p|}
< @!b:integer; {badness of current page}
---
> @!q,@!r:pointer; {nodes being examined}
> @!b,@!c:integer; {badness and cost of current page}
15813,15816c19311,19313
< @!wait:boolean; {should the present insertion be held over?}
< begin if (link(contrib_head)=null)∨ output_active then return;
< repeat continue: p←link(contrib_head);@/
< @<Update the value of |last_page_glue|@>;
---
> begin if (link(contrib_head)=null)or output_active then return;
> repeat continue: p:=link(contrib_head);@/
> @<Update the values of |last_glue|, |last_penalty|, and |last_kern|@>;
15818,15819c19315,19316
< put the nodes following the break back onto the contribution list,
< and |return| to the user's output routine if there is one@>;
---
> put the nodes following the break back onto the contribution list,
> and |return| to the user's output routine if there is one@>;
15827,15828c19324,19325
< if nest_ptr=0 then tail←contrib_head {vertical mode}
< else contrib_tail←contrib_head {other modes}
---
> if nest_ptr=0 then tail:=contrib_head {vertical mode}
> else contrib_tail:=contrib_head {other modes}
15830,15831c19327,19329
< @ @<Update the value of |last_page_glue|@>=
< if last_page_glue≠max_halfword then delete_glue_ref(last_page_glue);
---
> @ @<Update the values of |last_glue|...@>=
> if last_glue<>max_halfword then delete_glue_ref(last_glue);
> last_penalty:=0; last_kern:=0;
15833,15835c19331,19336
< begin last_page_glue←glue_ptr(p); add_glue_ref(last_page_glue);
< end
< else last_page_glue←max_halfword
---
> begin last_glue:=glue_ptr(p); add_glue_ref(last_glue);
> end
> else begin last_glue:=max_halfword;
> if type(p)=penalty_node then last_penalty:=penalty(p)
> else if type(p)=kern_node then last_kern:=width(p);
> end
15845,15848c19346,19349
< otherwise use node |p| to update the state of the current page;
< if this node is an insertion, |goto contribute|; otherwise if this node
< is not a legal breakpoint, |goto contribute| or |update_heights|;
< otherwise set |pi| to the penalty associated with this breakpoint@>;
---
> otherwise use node |p| to update the state of the current page;
> if this node is an insertion, |goto contribute|; otherwise if this node
> is not a legal breakpoint, |goto contribute| or |update_heights|;
> otherwise set |pi| to the penalty associated with this breakpoint@>;
15850,15852c19351,19353
< a page break, prepare for output, and either fire up the user's
< output routine and |return| or ship out the page and |goto done|@>;
< if (type(p)<glue_node)∨(type(p)>kern_node) then goto contribute;
---
> a page break, prepare for output, and either fire up the user's
> output routine and |return| or ship out the page and |goto done|@>;
> if (type(p)<glue_node)or(type(p)>kern_node) then goto contribute;
15854,15855c19355,19356
< glue or kern specified by node |p|@>;
< contribute: @<Make sure that |page_depth_max| is not exceeded@>;
---
> glue or kern specified by node~|p|@>;
> contribute: @<Make sure that |page_max_depth| is not exceeded@>;
15861,15862c19362,19363
< link(page_tail)←p; page_tail←p;
< link(contrib_head)←link(p); link(p)←null; goto done
---
> link(page_tail):=p; page_tail:=p;
> link(contrib_head):=link(p); link(p):=null; goto done
15865c19366
< link(contrib_head)←link(p); link(p)←null; flush_node_list(p)
---
> link(contrib_head):=link(p); link(p):=null; flush_node_list(p)
15875,15878c19376,19379
< @<Initialize the current page, insert the \.{\\topskip} glue
< ahead of |p|, and |goto continue|@>
< else @<Prepare to move a box or rule node to the current page,
< then |goto contribute|@>;
---
> @<Initialize the current page, insert the \.{\\topskip} glue
> ahead of |p|, and |goto continue|@>
> else @<Prepare to move a box or rule node to the current page,
> then |goto contribute|@>;
15880c19381
< then |goto contribute|@>;
---
> then |goto contribute|@>;
15882,15883c19383,19384
< else if precedes_break(page_tail) then pi←0
< else goto update_heights;
---
> else if precedes_break(page_tail) then pi:=0
> else goto update_heights;
15885,15888c19386,19389
< else if link(p)=null then return
< else if type(link(p))=glue_node then pi←0
< else goto update_heights;
< penalty_node: if page_contents<box_there then goto done1@+else pi←penalty(p);
---
> else if link(p)=null then return
> else if type(link(p))=glue_node then pi:=0
> else goto update_heights;
> penalty_node: if page_contents<box_there then goto done1@+else pi:=penalty(p);
15892c19393
< @:confusion page}{\quad page@>
---
> @:this can't happen page}{\quad page@>
15897,15901c19398,19402
< else page_contents←box_there;
< q←new_skip_param(top_skip_code); link(q)←p; {now |temp_ptr=glue_ptr(q)|}
< if width(temp_ptr)>height(p) then width(temp_ptr)←width(temp_ptr)-height(p)
< else width(temp_ptr)←0;
< link(q)←p; link(contrib_head)←q; goto continue;
---
> else page_contents:=box_there;
> q:=new_skip_param(top_skip_code); {now |temp_ptr=glue_ptr(q)|}
> if width(temp_ptr)>height(p) then width(temp_ptr):=width(temp_ptr)-height(p)
> else width(temp_ptr):=0;
> link(q):=p; link(contrib_head):=q; goto continue;
15905,15906c19406,19407
< begin cur_page_height←cur_page_height+cur_page_depth+height(p);
< cur_page_depth←depth(p);
---
> begin page_total:=page_total+page_depth+height(p);
> page_depth:=depth(p);
15910,15915c19411,19416
< @ @<Make sure that |page_depth_max| is not exceeded@>=
< if cur_page_depth>page_depth_max then
< begin cur_page_height←@|
< cur_page_height+cur_page_depth-page_depth_max;@/
< cur_page_depth←page_depth_max;
< end;
---
> @ @<Make sure that |page_max_depth| is not exceeded@>=
> if page_depth>page_max_depth then
> begin page_total:=@|
> page_total+page_depth-page_max_depth;@/
> page_depth:=page_max_depth;
> end;
15918,15925c19419,19426
< if type(p)=kern_node then q←p
< else begin q←glue_ptr(p);
< page_so_far[2+stretch_order(q)]←@|
< page_so_far[2+stretch_order(q)]+stretch(q);@/
< page_so_far[6]←page_so_far[6]+shrink(q);
< if (shrink_order(q)≠normal)∧(shrink(q)≠0) then
< begin@t@>@;@/
< print_nl("! Infinite glue shrinkage found on current page");@/
---
> if type(p)=kern_node then q:=p
> else begin q:=glue_ptr(p);
> page_so_far[2+stretch_order(q)]:=@|
> page_so_far[2+stretch_order(q)]+stretch(q);@/
> page_shrink:=page_shrink+shrink(q);
> if (shrink_order(q)<>normal)and(shrink(q)<>0) then
> begin@t@>@;@/
> print_err("Infinite glue shrinkage found on current page");@/
15927,15936c19428,19437
< help4("The page about to be output contains some infinitely")@/
< ("shrinkable glue, e.g., `\vss' or `\vskip 0pt minus 1fil'.")@/
< ("Such glue doesn't belong there; but you can safely proceed,")@/
< ("since the offensive shrinkability has been made finite.");
< error;
< r←new_spec(q); shrink_order(r)←normal; delete_glue_ref(q);
< glue_ptr(p)←r;
< end;
< end;
< cur_page_height←cur_page_height+cur_page_depth+width(q); cur_page_depth←0
---
> help4("The page about to be output contains some infinitely")@/
> ("shrinkable glue, e.g., `\vss' or `\vskip 0pt minus 1fil'.")@/
> ("Such glue doesn't belong there; but you can safely proceed,")@/
> ("since the offensive shrinkability has been made finite.");
> error;
> r:=new_spec(q); shrink_order(r):=normal; delete_glue_ref(q);
> glue_ptr(p):=r; q:=r;
> end;
> end;
> page_total:=page_total+page_depth+width(q); page_depth:=0
15940,15958c19441,19478
< begin @<Compute the badness, |b|, of the current page,
< using |awful_bad| if the box is too full@>;
< if b≤inf_bad then
< if pi≤eject_penalty then b←pi
< else b←b+pi+ins_penalties;
< if b≤least_page_badness then
< begin best_page_break←p; best_size←page_size;
< least_page_badness←b;
< r←link(page_ins_head);
< while r≠page_ins_head do
< begin best_ins_ptr(r)←last_ins_ptr(r);
< r←link(r);
< end;
< end;
< if (b=awful_bad)∨(pi≤eject_penalty) then
< @<Prepare to output the current page at the best place;
< then fire up the user's output routine and |return|,
< or ship out the page and |goto done|@>;
< end
---
> begin @<Compute the badness, |b|, of the current page,
> using |awful_bad| if the box is too full@>;
> if b<awful_bad then
> if pi<=eject_penalty then c:=pi
> else if b<inf_bad then c:=b+pi+insert_penalties
> else c:=deplorable
> else c:=b;
> if insert_penalties>=10000 then c:=awful_bad;
> @!stat if tracing_pages>0 then @<Display the page break cost@>;@+tats@;@/
> if c<=least_page_cost then
> begin best_page_break:=p; best_size:=page_goal;
> least_page_cost:=c;
> r:=link(page_ins_head);
> while r<>page_ins_head do
> begin best_ins_ptr(r):=last_ins_ptr(r);
> r:=link(r);
> end;
> end;
> if (c=awful_bad)or(pi<=eject_penalty) then
> begin fire_up(p); {output the current page at the best place}
> if output_active then return; {user's output routine will act}
> goto done; {the page has been shipped out by default output routine}
> end;
> end
>
> @ @<Display the page break cost@>=
> begin begin_diagnostic; print_nl("%");
> print(" t="); print_totals;@/
> print(" g="); print_scaled(page_goal);@/
> print(" b=");
> if b=awful_bad then print_char("*")@+else print_int(b);
> @.*\relax@>
> print(" p="); print_int(pi);
> print(" c=");
> if c=awful_bad then print_char("*")@+else print_int(c);
> if c<=least_page_cost then print_char("#");
> end_diagnostic(false);
> end
15961,15966c19481,19486
< if cur_page_height<page_size then
< if (page_so_far[3]≠0) or (page_so_far[4]≠0) or@|
< (page_so_far[5]≠0) then b←0
< else b←badness(page_size-cur_page_height,page_so_far[2])
< else if cur_page_height-page_size>page_so_far[6] then b←awful_bad
< else b←badness(cur_page_height-page_size,page_so_far[6])
---
> if page_total<page_goal then
> if (page_so_far[3]<>0) or (page_so_far[4]<>0) or@|
> (page_so_far[5]<>0) then b:=0
> else b:=badness(page_goal-page_total,page_so_far[2])
> else if page_total-page_goal>page_shrink then b:=awful_bad
> else b:=badness(page_total-page_goal,page_shrink)
15970,15987c19490,19508
< n←subtype(p); r←page_ins_head;
< while n≥subtype(link(r)) do r←link(r);
< n←qo(n);
< if subtype(r)≠qi(n) then
< @<Create a page insertion node with |subtype(r)=qi(n)|, and
< include the glue correction for box |n| in the
< current page state@>;
< if type(r)=inserting then
< begin last_ins_ptr(r)←p;
< delta←page_size-cur_page_height+page_so_far[6];
< {this much room is left if we shrink the maximum}
< h←x_over_n(width(p),1000)*count(n); {this much room is needed}
< if (h≤delta)∧(width(p)+width(r)≤dimen(n)) then
< begin page_size←page_size-h; width(r)←width(r)+width(p);
< end
< else @<Find the best way to split the insertion, and change
< |type(r)| to |split_up|@>;
< end;
---
> n:=subtype(p); r:=page_ins_head;
> while n>=subtype(link(r)) do r:=link(r);
> n:=qo(n);
> if subtype(r)<>qi(n) then
> @<Create a page insertion node with |subtype(r)=qi(n)|, and
> include the glue correction for box |n| in the
> current page state@>;
> if type(r)=split_up then insert_penalties:=insert_penalties+float_cost(p)
> else begin last_ins_ptr(r):=p;
> delta:=page_goal-page_total-page_depth+page_shrink;
> {this much room is left if we shrink the maximum}
> if count(n)=1000 then h:=height(p)
> else h:=x_over_n(height(p),1000)*count(n); {this much room is needed}
> if ((h<=0)or(h<=delta))and(height(p)+height(r)<=dimen(n)) then
> begin page_goal:=page_goal-h; height(r):=height(r)+height(p);
> end
> else @<Find the best way to split the insertion, and change
> |type(r)| to |split_up|@>;
> end;
15992,15994c19513,19515
< of \.{\\box}@@|n| only when the first \.{\\insert}@@|n| node is
< encountered for a new page. A user who changes the contents of \.{\\box}@@|n|
< after that first \.{\\insert}@@|n| had better be either extremely careful
---
> of \.{\\box}~|n| only when the first \.{\\insert}~|n| node is
> encountered for a new page. A user who changes the contents of \.{\\box}~|n|
> after that first \.{\\insert}~|n| had better be either extremely careful
15998,16016c19519,19538
< begin q←get_node(page_ins_node_size); link(q)←link(r); link(r)←q; r←q;
< subtype(r)←qi(n); type(r)←inserting;
< if box(n)=null then width(r)←0
< else width(r)←height(box(n))+depth(box(n));
< best_ins_ptr(r)←null;@/
< q←skip(n); h←x_over_n(width(r),1000)*count(n);
< page_size←page_size-h;@/
< page_so_far[1]←page_so_far[1]+width(q);@/
< page_so_far[2+stretch_order(q)]←@|page_so_far[2+stretch_order(q)]+stretch(q);@/
< page_so_far[6]←page_so_far[6]+shrink(q);
< if (shrink_order(q)≠normal)∧(shrink(q)≠0) then
< begin print_nl("! Infinite glue shrinkage inserted from \skip ");
< @.Infinite glue shrinkage@>
< print_int(n);
< help3("The correction glue for page breaking with insertions")@/
< ("must have finite shrinkability. But you may proceed,")@/
< ("since the offensive shrinkability has been made finite.");
< error;
< end;
---
> begin q:=get_node(page_ins_node_size); link(q):=link(r); link(r):=q; r:=q;
> subtype(r):=qi(n); type(r):=inserting; ensure_vbox(n);
> if box(n)=null then height(r):=0
> else height(r):=height(box(n))+depth(box(n));
> best_ins_ptr(r):=null;@/
> q:=skip(n);
> if count(n)=1000 then h:=height(r)
> else h:=x_over_n(height(r),1000)*count(n);
> page_goal:=page_goal-h-width(q);@/
> page_so_far[2+stretch_order(q)]:=@|page_so_far[2+stretch_order(q)]+stretch(q);@/
> page_shrink:=page_shrink+shrink(q);
> if (shrink_order(q)<>normal)and(shrink(q)<>0) then
> begin print_err("Infinite glue shrinkage inserted from "); print_esc("skip");
> @.Infinite glue shrinkage...@>
> print_int(n);
> help3("The correction glue for page breaking with insertions")@/
> ("must have finite shrinkability. But you may proceed,")@/
> ("since the offensive shrinkability has been made finite.");
> error;
> end;
16030,16039c19552,19577
< begin if count(n)≤0 then w←max_dimen
< else w←x_over_n(delta,count(n))*1000;
< if w>dimen(n)-width(r) then w←dimen(n)-width(r);
< q←vert_break(ins_ptr(p),w);
< width(r)←width(r)+best_height_plus_depth;
< best_height_plus_depth←x_over_n(best_height_plus_depth,1000)*count(n);
< page_size←page_size-best_height_plus_depth;
< type(r)←split_up; broken_ptr(r)←q; broken_ins(r)←p;
< if q=null then ins_penalties←ins_penalties+eject_penalty
< else if type(q)=penalty_node then ins_penalties←ins_penalties+penalty(q);
---
> begin if count(n)<=0 then w:=max_dimen
> else begin w:=page_goal-page_total-page_depth;
> if count(n)<>1000 then w:=x_over_n(w,count(n))*1000;
> end;
> if w>dimen(n)-height(r) then w:=dimen(n)-height(r);
> q:=vert_break(ins_ptr(p),w,depth(p));
> height(r):=height(r)+best_height_plus_depth;
> @!stat if tracing_pages>0 then @<Display the insertion split cost@>;@+tats@;@/
> if count(n)<>1000 then
> best_height_plus_depth:=x_over_n(best_height_plus_depth,1000)*count(n);
> page_goal:=page_goal-best_height_plus_depth;
> type(r):=split_up; broken_ptr(r):=q; broken_ins(r):=p;
> if q=null then insert_penalties:=insert_penalties+eject_penalty
> else if type(q)=penalty_node then insert_penalties:=insert_penalties+penalty(q);
> end
>
> @ @<Display the insertion split cost@>=
> begin begin_diagnostic; print_nl("% split"); print_int(n);
> @.split@>
> print(" to "); print_scaled(w);
> print_char(","); print_scaled(best_height_plus_depth);@/
> print(" p=");
> if q=null then print_int(eject_penalty)
> else if type(q)=penalty_node then print_int(penalty(q))
> else print_char("0");
> end_diagnostic(false);
16048,16049c19586,19593
< program uses the fact that |bot_mark≠null| implies |first_mark≠null|;
< |bot_mark=null| implies |top_mark=first_mark=null|.
---
> program uses the fact that |bot_mark<>null| implies |first_mark<>null|;
> it also knows that |bot_mark=null| implies |top_mark=first_mark=null|.
>
> The |fire_up| subroutine prepares to output the current page at the best
> place; then it fires up the user's output routine, if there is one,
> or it simply ships out the page. There is one parameter, |c|, which represents
> the node that was being contributed to the page when the decision to
> force an output was made.
16051c19595,19604
< @<Prepare to output the current page at the best place...@>=
---
> @<Declare the procedure called |fire_up|@>=
> procedure fire_up(@!c:pointer);
> label exit;
> var p,@!q,@!r,@!s:pointer; {nodes being examined and/or changed}
> @!prev_p:pointer; {predecessor of |p|}
> @!n:min_quarterword..255; {insertion box number}
> @!wait:boolean; {should the present insertion be held over?}
> @!save_vbadness:integer; {saved value of |vbadness|}
> @!save_vfuzz: scaled; {saved value of |vfuzz|}
> @!save_split_top_skip: pointer; {saved value of |split_top_skip|}
16053,16057c19606,19610
< if bot_mark≠null then
< begin if top_mark≠null then delete_token_ref(top_mark);
< top_mark←bot_mark; add_token_ref(top_mark);
< delete_token_ref(first_mark); first_mark←null;
< end;
---
> if bot_mark<>null then
> begin if top_mark<>null then delete_token_ref(top_mark);
> top_mark:=bot_mark; add_token_ref(top_mark);
> delete_token_ref(first_mark); first_mark:=null;
> end;
16059,16066c19612,19622
< |bot_mark|, append insertions to their boxes, and put the
< remaining nodes back on the contribution list@>;
< if (top_mark≠null)∧(first_mark=null) then
< begin first_mark←top_mark; add_token_ref(top_mark);
< end;
< if output_routine≠null then @<Fire up the user's output routine and |return|@>
< else @<Perform the default output routine and |goto done|@>;
< end
---
> |bot_mark|, append insertions to their boxes, and put the
> remaining nodes back on the contribution list@>;
> if (top_mark<>null)and(first_mark=null) then
> begin first_mark:=top_mark; add_token_ref(top_mark);
> end;
> if output_routine<>null then
> if dead_cycles>=max_dead_cycles then
> @<Explain that too many dead cycles have occurred in a row@>
> else @<Fire up the user's output routine and |return|@>;
> @<Perform the default output routine@>;
> exit:end;
16070,16072c19626,19628
< begin geq_word_define(int_base+output_penalty_code,penalty(best_page_break));
< penalty(best_page_break)←inf_penalty;
< end
---
> begin geq_word_define(int_base+output_penalty_code,penalty(best_page_break));
> penalty(best_page_break):=inf_penalty;
> end
16075,16076c19631,19633
< @ As the page is finally being prepared for output, |p| runs through the vlist,
< with |prev_p| trailing behind, and |q| is the tail of a list of insertions that
---
> @ As the page is finally being prepared for output,
> pointer |p| runs through the vlist, with |prev_p| trailing behind;
> pointer |q| is the tail of a list of insertions that
16080c19637
< if p=best_page_break then best_page_break←null; {|p| not yet linked in}
---
> if c=best_page_break then best_page_break:=null; {|c| not yet linked in}
16082,16095c19639,19658
< @<Prepare all the boxes involved in insertions to act as queues@>;
< q←hold_head; link(q)←null; prev_p←page_head; p←link(prev_p);
< while p≠best_page_break do
< begin if type(p)=ins_node then @<Either insert the material
< specified by node |p| into the appropriate box, or
< hold it for the next page; also delete node |p| from
< the current page@>
< else if type(p)=mark_node then @<Update the values of
< |first_mark| and |bot_mark|@>;
< prev_p←p; p←link(prev_p);
< end;
< @<Break the current page at node |p|, put it in box@@255,
< and put the remaining nodes on the contribution list@>;
< @<Delete the page-insertion nodes@>
---
> insert_penalties:=0; {this will count the number of insertions held over}
> save_split_top_skip:=split_top_skip;
> if holding_inserts<=0 then
> @<Prepare all the boxes involved in insertions to act as queues@>;
> q:=hold_head; link(q):=null; prev_p:=page_head; p:=link(prev_p);
> while p<>best_page_break do
> begin if type(p)=ins_node then
> begin if holding_inserts<=0 then
> @<Either insert the material specified by node |p| into the
> appropriate box, or hold it for the next page;
> also delete node |p| from the current page@>;
> end
> else if type(p)=mark_node then @<Update the values of
> |first_mark| and |bot_mark|@>;
> prev_p:=p; p:=link(prev_p);
> end;
> split_top_skip:=save_split_top_skip;
> @<Break the current page at node |p|, put it in box~255,
> and put the remaining nodes on the contribution list@>;
> @<Delete \(t)the page-insertion nodes@>
16098,16104c19661,19667
< if box(255)≠null then
< begin print_nl("! \box255 is not absent");
< @:box255}{\.{\\box255 is not absent}@>
< help2("You shouldn't use \box255 except in \output routines.")@/
< ("Proceed, and I'll discard its present contents.");
< error; flush_node_list(box(255)); box(255)←null;
< end
---
> if box(255)<>null then
> begin print_err(""); print_esc("box"); print("255 is not void");
> @:box255}{\.{\\box255 is not void}@>
> help2("You shouldn't use \box255 except in \output routines.")@/
> ("Proceed, and I'll discard its present contents.");
> box_error(255);
> end
16108,16112c19671,19675
< begin first_mark←mark_ptr(p);
< add_token_ref(first_mark);
< end;
< if bot_mark≠null then delete_token_ref(bot_mark);
< bot_mark←mark_ptr(p); add_token_ref(bot_mark);
---
> begin first_mark:=mark_ptr(p);
> add_token_ref(first_mark);
> end;
> if bot_mark<>null then delete_token_ref(bot_mark);
> bot_mark:=mark_ptr(p); add_token_ref(bot_mark);
16121c19684,19687
< and only if |prev_p=page_tail|.
---
> and only if |prev_p=page_tail|. Error messages are suppressed within
> |vpackage|, since the box might appear to be overfull or underfull simply
> because the stretch and shrink from the \.{\\skip} registers for inserts
> are not actually present in the box.
16124,16136c19690,19706
< if p≠null then
< begin if link(contrib_head)=null then
< if nest_ptr=0 then tail←page_tail
< else contrib_tail←page_tail;
< link(page_tail)←link(contrib_head);
< link(contrib_head)←p;
< link(prev_p)←null;
< end;
< box(255)←vpackage(link(page_head),best_size,exactly,page_depth_max);
< @<Start a new current page@>;
< if q≠hold_head then
< begin link(page_head)←link(hold_head); page_tail←q;
< end
---
> if p<>null then
> begin if link(contrib_head)=null then
> if nest_ptr=0 then tail:=page_tail
> else contrib_tail:=page_tail;
> link(page_tail):=link(contrib_head);
> link(contrib_head):=p;
> link(prev_p):=null;
> end;
> save_vbadness:=vbadness; vbadness:=inf_bad;
> save_vfuzz:=vfuzz; vfuzz:=max_dimen; {inhibit error messages}
> box(255):=vpackage(link(page_head),best_size,exactly,page_max_depth);
> vbadness:=save_vbadness; vfuzz:=save_vfuzz;
> if last_glue<>max_halfword then delete_glue_ref(last_glue);
> @<Start a new current page@>; {this sets |last_glue:=max_halfword|}
> if q<>hold_head then
> begin link(page_head):=link(hold_head); page_tail:=q;
> end
16145,16162c19715,19733
< r←link(page_ins_head);
< while r≠page_ins_head do
< begin if best_ins_ptr(r)≠null then
< begin n←qo(subtype(r));
< if box(n)=null then box(n)←new_null_box;
< p←box(n)+list_offset;
< while link(p)≠null do p←link(p);
< last_ins_ptr(r)←p;
< end;
< r←link(r);
< end
<
< @ @<Delete the page-insertion nodes@>=
< r←link(page_ins_head);
< while r≠page_ins_head do
< begin q←link(r); free_node(r,page_ins_node_size); r←q;
< end;
< link(page_ins_head)←page_ins_head
---
> begin r:=link(page_ins_head);
> while r<>page_ins_head do
> begin if best_ins_ptr(r)<>null then
> begin n:=qo(subtype(r)); ensure_vbox(n);
> if box(n)=null then box(n):=new_null_box;
> p:=box(n)+list_offset;
> while link(p)<>null do p:=link(p);
> last_ins_ptr(r):=p;
> end;
> r:=link(r);
> end;
> end
>
> @ @<Delete \(t)the page-insertion nodes@>=
> r:=link(page_ins_head);
> while r<>page_ins_head do
> begin q:=link(r); free_node(r,page_ins_node_size); r:=q;
> end;
> link(page_ins_head):=page_ins_head
16164,16165c19735,19736
< @ We will set |best_ins_ptr←null| and package the box corresponding to
< insertion node@@|r|, just after making the final insertion into that box.
---
> @ We will set |best_ins_ptr:=null| and package the box corresponding to
> insertion node~|r|, just after making the final insertion into that box.
16170,16182c19741,19752
< begin r←link(page_ins_head);
< while subtype(r)≠subtype(p) do r←link(r);
< if best_ins_ptr(r)=null then wait←true
< else begin wait←false; s←ins_ptr(p); {|s≠null|}
< link(last_ins_ptr(r))←s; s←last_ins_ptr(r);
< if best_ins_ptr(r)=p then
< @<Wrap up the box specified by node |r|, splitting node |p| if
< called for; set |wait←true| if node |p| holds a remainder after
< splitting@>
< else begin while link(s)≠null do s←link(s);
< last_ins_ptr(r)←s;
< end;
< end;
---
> begin r:=link(page_ins_head);
> while subtype(r)<>subtype(p) do r:=link(r);
> if best_ins_ptr(r)=null then wait:=true
> else begin wait:=false; s:=last_ins_ptr(r); link(s):=ins_ptr(p);
> if best_ins_ptr(r)=p then
> @<Wrap up the box specified by node |r|, splitting node |p| if
> called for; set |wait:=true| if node |p| holds a remainder after
> splitting@>
> else begin while link(s)<>null do s:=link(s);
> last_ins_ptr(r):=s;
> end;
> end;
16184c19754
< from the current page, or delete |node(p)|@>;
---
> from the current page, or delete |node(p)|@>;
16189,16201c19759,19772
< if (broken_ins(r)=p)∧(broken_ptr(r)≠null) then
< begin while link(s)≠broken_ptr(r) do s←link(s);
< ins_ptr(p)←prune_page_top(broken_ptr(r));
< if ins_ptr(p)≠null then
< begin temp_ptr←vpack(ins_ptr(p),natural);
< width(p)←height(temp_ptr)+depth(temp_ptr);
< free_node(temp_ptr,box_node_size);
< end;
< link(s)←null; wait←(ins_ptr(p)≠null);
< end;
< best_ins_ptr(r)←null;
< n←qo(subtype(r));
< temp_ptr←list_ptr(box(n));
---
> if (broken_ins(r)=p)and(broken_ptr(r)<>null) then
> begin while link(s)<>broken_ptr(r) do s:=link(s);
> link(s):=null;
> split_top_skip:=split_top_ptr(p);
> ins_ptr(p):=prune_page_top(broken_ptr(r));
> if ins_ptr(p)<>null then
> begin temp_ptr:=vpack(ins_ptr(p),natural);
> height(p):=height(temp_ptr)+depth(temp_ptr);
> free_node(temp_ptr,box_node_size); wait:=true;
> end;
> end;
> best_ins_ptr(r):=null;
> n:=qo(subtype(r));
> temp_ptr:=list_ptr(box(n));
16203c19774
< box(n)←vpack(temp_ptr,natural);
---
> box(n):=vpack(temp_ptr,natural);
16207c19778
< link(prev_p)←link(p); link(p)←null;
---
> link(prev_p):=link(p); link(p):=null;
16209,16212c19780,19785
< begin link(q)←p; q←p;
< end
< else free_node(p,ins_node_size);
< p←prev_p
---
> begin link(q):=p; q:=p; incr(insert_penalties);
> end
> else begin delete_glue_ref(split_top_ptr(p));
> free_node(p,ins_node_size);
> end;
> p:=prev_p
16218,16226c19791,19808
< @<Perform the default output routine and |goto done|@>=
< begin if link(page_head)≠null then
< begin if link(contrib_head)=null then
< if nest_ptr=0 then tail←page_tail@+else contrib_tail←page_tail
< else link(page_tail)←link(contrib_head);
< link(contrib_head)←link(page_head);
< link(page_head)←null; page_tail←page_head;
< end;
< ship_out(box(255)); box(255)←null; goto done;
---
> @<Perform the default output routine@>=
> begin if link(page_head)<>null then
> begin if link(contrib_head)=null then
> if nest_ptr=0 then tail:=page_tail@+else contrib_tail:=page_tail
> else link(page_tail):=link(contrib_head);
> link(contrib_head):=link(page_head);
> link(page_head):=null; page_tail:=page_head;
> end;
> ship_out(box(255)); box(255):=null;
> end
>
> @ @<Explain that too many dead cycles have occurred in a row@>=
> begin print_err("Output loop---"); print_int(dead_cycles);
> @.Output loop...@>
> print(" consecutive dead cycles");
> help3("I've concluded that your \output is awry; it never does a")@/
> ("\shipout, so I'm shipping \box255 out myself. Next time")@/
> ("increase \maxdeadcycles if you want me to be more patient!"); error;
16230,16231c19812,19814
< begin output_active←true;
< push_nest; mode←-vmode; prev_depth←ignore_depth; mode_line←-line;
---
> begin output_active:=true;
> incr(dead_cycles);
> push_nest; mode:=-vmode; prev_depth:=ignore_depth; mode_line:=-line;
16233c19816
< new_save_level(output_group);
---
> new_save_level(output_group); normal_paragraph;
16242c19825,19829
< begin end_graf; unsave; output_active←false;
---
> begin if (loc<>null) or
> ((token_type<>output_text)and(token_type<>backed_up)) then
> @<Recover from an unbalanced output routine@>;
> end_token_list; {conserve stack space in case more outputs are triggered}
> end_graf; unsave; output_active:=false; insert_penalties:=0;@/
16244,16253c19831,19840
< if tail≠head then {current list goes after heldover insertions}
< begin link(page_tail)←link(head);
< page_tail←tail;
< end;
< if link(page_head)≠null then {and both go before heldover contributions}
< begin if link(contrib_head)=null then contrib_tail←page_tail;
< link(page_tail)←link(contrib_head);
< link(contrib_head)←link(page_head);
< link(page_head)←null; page_tail←page_head;
< end;
---
> if tail<>head then {current list goes after heldover insertions}
> begin link(page_tail):=link(head);
> page_tail:=tail;
> end;
> if link(page_head)<>null then {and both go before heldover contributions}
> begin if link(contrib_head)=null then contrib_tail:=page_tail;
> link(page_tail):=link(contrib_head);
> link(contrib_head):=link(page_head);
> link(page_head):=null; page_tail:=page_head;
> end;
16256a19844,19852
> @ @<Recover from an unbalanced output routine@>=
> begin print_err("Unbalanced output routine");
> @.Unbalanced output routine@>
> help2("Your sneaky output routine has problematic {'s and/or }'s.")@/
> ("I can't handle that very well; good luck."); error;
> repeat get_token;
> until loc=null;
> end {loops forever if reading from a file, since |null=min_halfword<=0|}
>
16258,16267c19854,19864
< if box(255)≠null then
< begin help3("Your \output commands should empty \box255,")@/
< ("e.g., by saying `\shipout\box255'.")@/
< ("Proceed; I'll discard its present contents.");
< print_nl("! \output routine didn't use all of \box255"); error;
< @:output_}{\.{\\output routine didn't use...}@>
< flush_node_list(box(255)); box(255)←null;
< end
< @* \[45] The chief executive.
< \hskip-1.1pt % avoid overfull box in published version!
---
> if box(255)<>null then
> begin print_err("Output routine didn't use all of ");
> print_esc("box"); print_int(255);
> @.Output routine didn't use...@>
> help3("Your \output commands should empty \box255,")@/
> ("e.g., by saying `\shipout\box255'.")@/
> ("Proceed; I'll discard its present contents.");
> box_error(255);
> end
>
> @* \[46] The chief executive.
16272c19869
< In a sense, this is the grand climax of the program: It applies all of the
---
> In a sense, this is the grand climax of the program: It applies all the
16283,16284c19880,19881
< using |get_nc_token|. Then the program branches at high speed into one of
< about 100 possible directions, based on the the value of the current
---
> using |get_x_token|. Then the program branches at high speed into one of
> about 100 possible directions, based on the value of the current
16307c19904
< with special care.
---
> with particular care.
16314,16318c19911,19917
< @d main_loop=70 {go here to typeset |cur_chr| in the current font}
< @d main_loop_1=71 {like |main_loop|, but |(f,c)| = current font and char}
< @d main_loop_2=72 {like |main_loop_1|, but |c| is known to be in range}
< @d main_loop_3=73 {like |main_loop_2|, but several variables are set up}
< @d append_normal_space=74 {go here to append a normal space between words}
---
> @d main_loop=70 {go here to typeset a string of consecutive characters}
> @d main_loop_wrapup=80 {go here to finish a character or ligature}
> @d main_loop_move=90 {go here to advance the ligature cursor}
> @d main_loop_move_lig=95 {same, when advancing past a generated ligature}
> @d main_loop_lookahead=100 {go here to bring in another character, if any}
> @d main_lig_loop=110 {go here to check for ligatures or kerning}
> @d append_normal_space=120 {go here to append a normal space between words}
16323,16327c19922,19929
< label big_switch,reswitch,main_loop,main_loop_1,main_loop_2,main_loop_3,
< append_normal_space,exit;
< var t:integer; {general-purpose temporary variable}
< @<Local variables for the inner loop of |main_control|@>@;
< begin big_switch: get_nc_token;@/
---
> label big_switch,reswitch,main_loop,main_loop_wrapup,
> main_loop_move,main_loop_move+1,main_loop_move+2,main_loop_move_lig,
> main_loop_lookahead,main_loop_lookahead+1,
> main_lig_loop,main_lig_loop+1,main_lig_loop+2,
> append_normal_space,exit;
> var@!t:integer; {general-purpose temporary variable}
> begin if every_job<>null then begin_token_list(every_job,every_job_text);
> big_switch: get_x_token;@/
16330,16332c19932,19938
< hmode+letter,hmode+other_char: goto main_loop;
< hmode+char_num: begin scan_char_num; cur_chr←cur_val; goto main_loop;
< end;
---
> hmode+letter,hmode+other_char,hmode+char_given: goto main_loop;
> hmode+char_num: begin scan_char_num; cur_chr:=cur_val; goto main_loop;@+end;
> hmode+no_boundary: begin get_x_token;
> if (cur_cmd=letter)or(cur_cmd=other_char)or(cur_cmd=char_given)or
> (cur_cmd=char_num) then cancel_boundary:=true;
> goto reswitch;
> end;
16334c19940
< else app_space;
---
> else app_space;
16339,16341c19945,19947
< main_loop:@<Append character |cur_chr| and the following characters (if@@any)
< to the current hlist in the current font; |goto reswitch| when
< a non-character has been fetched@>;
---
> main_loop:@<Append character |cur_chr| and the following characters (if~any)
> to the current hlist in the current font; |goto reswitch| when
> a non-character has been fetched@>;
16343c19949
< then |goto big_switch|@>;
---
> then |goto big_switch|@>;
16351,16399c19957,19979
< if interrupt>0 then if OK_to_interrupt then
< begin back_input; pause_for_instructions; goto big_switch;
< end;
< debug if panicking then check_mem(false);@+@;@+gubed
< if tracing_commands≠0 then
< begin begin_diagnostic; print_nl("{");
< if mode≠shown_mode then
< begin print_mode(mode); print(": "); shown_mode←mode;
< end;
< print_cmd_chr(cur_cmd,cur_chr); print_char("}");
< end_diagnostic;
< end
<
< @ In the following program, |l| is the current character or ligature;
< it might grow into a longer ligature. One or more characters has been
< used to define |l|, and the last of these was |c|. The chief use of |c|
< will be to modify |space_factor| and to insert discretionary nodes after
< explicit hyphens in the text.
<
< @<Local variables for the inner loop of |main_control|@>=
< @!l:quarterword; {the current character or ligature}
< @!c:eight_bits; {the most recent character}
< @!f:internal_font_number; {the current font}
< @!r:halfword; {the next character for ligature/kern matching}
< @!p:pointer; {the current |char_node|}
< @!k:0..font_mem_size; {index into |font_info|}
< @!q:pointer; {where a ligature should be detached}
< @!i:four_quarters; {character information bytes for |l|}
< @!j:four_quarters; {ligature/kern command}
< @!s:integer; {space factor code}
< @!ligature_present:boolean; {should a ligature node be made?}
<
< @ @<Append character |cur_chr| and the following characters...@>=
< @^inner loop@>
< f←cur_font;
< if f=undefined_font then
< begin missing_font; goto big_switch;
< end;
< c←cur_chr;
< main_loop_1: if (c<font_bc[f])∨(c>font_ec[f]) then
< b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment