// Code generated by '[/tmp/go-build3521972418/b001/exe/generate]', DO NOT EDIT. // % This program is copyright (C) 1982 by D. E. Knuth; all rights are reserved. // % Unlimited copying and redistribution of this file are permitted as long // % as this file is not modified. Modifications are permitted, but only if // % the resulting file is not named tex.web. (The WEB system provides // % for alterations via an auxiliary file; the master file should stay intact.) // % See Appendix H of the WEB manual for hints on how to install this program. // % And see Appendix A of the TRIP manual for details about how to validate it. // // % TeX is a trademark of the American Mathematical Society. // % METAFONT is a trademark of Addison-Wesley Publishing Company. // // % Version 0 was released in September 1982 after it passed a variety of tests. // % Version 1 was released in November 1983 after thorough testing. // % Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984). // % Version 1.2 allowed `0' in response to an error, et alia (October 1984). // % Version 1.3 made memory allocation more flexible and local (November 1984). // % Version 1.4 fixed accents right after line breaks, et alia (April 1985). // % Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985). // % Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986). // % Version 2.1 corrected anomalies in discretionary breaks (January 1987). // % Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987). // % Version 2.3 avoided incomplete page in premature termination (August 1987). // % Version 2.4 fixed \noaligned rules in indented displays (August 1987). // % Version 2.5 saved cur_order when expanding tokens (September 1987). // % Version 2.6 added 10sp slop when shipping leaders (November 1987). // % Version 2.7 improved rounding of negative-width characters (November 1987). // % Version 2.8 fixed weird bug if no \patterns are used (December 1987). // % Version 2.9 made \csname\endcsname's "relax" local (December 1987). // % Version 2.91 fixed \outer\def\a0[]\a\a bug (April 1988). // % Version 2.92 fixed \patterns, also file names with complex macros (May 1988). // % Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988). // % Version 2.94 kept open_log_file from calling fatal_error (November 1988). // % Version 2.95 solved that problem a better way (December 1988). // % Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989). // % Version 2.97 corrected blunder in creating 2.95 (February 1989). // % Version 2.98 omitted save_for_after at outer level (March 1989). // % Version 2.99 caught $$\begingroup\halign..$$ (June 1989). // % Version 2.991 caught .5\ifdim.6... (June 1989). // % Version 2.992 introduced major changes for 8-bit extensions (September 1989). // % Version 2.993 fixed a save_stack synchronization bug et alia (December 1989). // % Version 3.0 fixed unusual displays; was more \output robust (March 1990). // % Version 3.1 fixed nullfont, disabled \write[\the\prevgraf] (September 1990). // % Version 3.14 fixed unprintable font names and corrected typos (March 1991). // % Version 3.141 more of same; reconstituted ligatures better (March 1992). // % Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993). // % Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995). // % Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002). // % Version 3.1415926 was a general cleanup with minor fixes (February 2008). // % Version 3.14159265 was similar (January 2014). // % Version 3.141592653 was similar but more extensive (January 2021). // // % A reward of $327.68 will be paid to the first finder of any remaining bug. // // % Although considerable effort has been expended to make the TeX program // % correct and reliable, no warranty is implied; the author disclaims any // % obligation or liability for damages, including but not limited to // % special, indirect, or consequential damages arising out of or in // % connection with the use or performance of this software. This work has // % been a ``labor of love'' and the author hopes that users enjoy it. // // % Here is TeX material that gets inserted after \input webmac // \def\hang[\hangindent 3em\noindent\ignorespaces] // \def\hangg#1 [\hang\hbox[#1 ]] // \def\textindent#1[\hangindent2.5em\noindent\hbox to2.5em[\hss#1 ]\ignorespaces] // \font\ninerm=cmr9 // \let\mc=\ninerm % medium caps for names like SAIL // \def\PASCAL[Pascal] // \def\ph[\hbox[Pascal-H]] // \def\pct![[\char`\%]] % percent sign in ordinary text // \font\logo=logo10 % font used for the METAFONT logo // \def\MF[[\logo META]\-[\logo FONT]] // \def\<#1>[$\langle#1\rangle$] // \def\section[\mathhexbox278] // // \def\(#1)[] % this is used to make section names sort themselves better // \def\9#1[] % this is used for sort keys in the index via @:sort key][entry@> // // \outer\def\N#1. \[#2]#3.[\MN#1.\vfil\eject % begin starred section // \def\rhead[PART #2:\uppercase[#3]] % define running headline // \message[*\modno] % progress report // \edef\next[\write\cont[\Z[\?#2]#3][\modno][\the\pageno]]]\next // \ifon\startsection[\bf\ignorespaces#3.\quad]\ignorespaces] // \let\?=\relax % we want to be able to \write a \? // // \def\title[\TeX82] // \def\topofcontents[\hsize 5.5in // \vglue 0pt plus 1fil minus 1.5in // \def\?##1][\hbox to 1in[\hfil##1.\ ]] // ] // \def\botofcontents[\vskip 0pt plus 1fil minus 1.5in] // \pageno=3 // \def\glob[13] % this should be the section number of "" // \def\gglob[20, 26] % this should be the next two sections of "" // // // 1. \[1] Introduction // tangle:pos tex.web:95:22: // This is \TeX, a document compiler intended to produce typesetting of high // quality. // The \PASCAL\ program that follows is the definition of \TeX82, a standard // \xref[PASCAL][\PASCAL] // \xref[TeX82][\TeX82] // version of \TeX\ that is designed to be highly portable so that identical output // will be obtainable on a great variety of computers. // // The main purpose of the following program is to explain the algorithms of \TeX\ // as clearly as possible. As a result, the program will not necessarily be very // efficient when a particular \PASCAL\ compiler has translated it into a // particular machine language. However, the program has been written so that it // can be tuned to run efficiently in a wide variety of operating environments // by making comparatively few changes. Such flexibility is possible because // the documentation that follows is written in the \.[WEB] language, which is // at a higher level than \PASCAL; the preprocessing step that converts \.[WEB] // to \PASCAL\ is able to introduce most of the necessary refinements. // Semi-automatic translation to other languages is also feasible, because the // program below does not make extensive use of features that are peculiar to // \PASCAL. // // A large piece of software like \TeX\ has inherent complexity that cannot // be reduced below a certain level of difficulty, although each individual // part is fairly simple by itself. The \.[WEB] language is intended to make // the algorithms as readable as possible, by reflecting the way the // individual program pieces fit together and by providing the // cross-references that connect different parts. Detailed comments about // what is going on, and about why things were done in certain ways, have // been liberally sprinkled throughout the program. These comments explain // features of the implementation, but they rarely attempt to explain the // \TeX\ language itself, since the reader is supposed to be familiar with // [\sl The \TeX book]. // \xref[WEB] // \xref[TeXbook][\sl The \TeX book] // 2. // tangle:pos tex.web:131:3: // The present implementation has a long ancestry, beginning in the summer // of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded // a prototype // \xref[Plass, Michael Frederick] // \xref[Liang, Franklin Mark] // \xref[Knuth, Donald Ervin] // based on some specifications that the author had made in May of that year. // This original proto\TeX\ included macro definitions and elementary // manipulations on boxes and glue, but it did not have line-breaking, // page-breaking, mathematical formulas, alignment routines, error recovery, // or the present semantic nest; furthermore, // it used character lists instead of token lists, so that a control sequence // like \.[\\halign] was represented by a list of seven characters. A // complete version of \TeX\ was designed and coded by the author in late // 1977 and early 1978; that program, like its prototype, was written in the // [\mc SAIL] language, for which an excellent debugging system was // available. Preliminary plans to convert the [\mc SAIL] code into a form // somewhat like the present ``web'' were developed by Luis Trabb~Pardo and // \xref[Trabb Pardo, Luis Isidoro] // the author at the beginning of 1979, and a complete implementation was // created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which // \xref[Zabala Salelles, Ignacio Andr\'es] // was written by the author during the latter part of 1981 and the early // part of 1982, also incorporates ideas from the 1979 implementation of // \xref[Guibas, Leonidas Ioannis] // \xref[Sedgewick, Robert] // \xref[Wyatt, Douglas Kirk] // \TeX\ in [\mc MESA] that was written by Leonidas Guibas, Robert Sedgewick, // and Douglas Wyatt at the Xerox Palo Alto Research Center. Several hundred // refinements were introduced into \TeX82 based on the experiences gained with // the original implementations, so that essentially every part of the system // 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. // \xref[Fuchs, David Raymond] // \xref[Trickey, Howard Wellington] // \xref[Ferguson, Michael John] // // No doubt there still is plenty of room for improvement, but the author // is firmly committed to keeping \TeX82 ``frozen'' from now on; stability // and reliability are to be its main virtues. // // On the other hand, the \.[WEB] description can be extended without changing // the core of \TeX82 itself, and the program has been designed so that such // extensions are not extremely difficult to make. // The |banner| string defined here should be changed whenever \TeX\ // undergoes any modifications, so that it will be clear which version of // \TeX\ might be the guilty party when a problem arises. // \xref[extensions to \TeX] // \xref[system dependencies] // // 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]. // 3. // tangle:pos tex.web:195:3: // Different \PASCAL s have slightly different conventions, and the present // \xref[PASCAL H][\ph] // program expresses \TeX\ in terms of the \PASCAL\ that was // available to the author in 1982. Constructions that apply to // this particular compiler, which we shall call \ph, should help the // reader see how to make an appropriate interface for other systems // if necessary. (\ph\ is Charles Hedrick's modification of a compiler // \xref[Hedrick, Charles Locke] // for the DECsystem-10 that was originally developed at the University of // Hamburg; cf.\ [\sl Software---Practice and Experience \bf6] (1976), // 29--42. The \TeX\ program below is intended to be adaptable, without // extensive changes, to most other versions of \PASCAL, so it does not fully // use the admirable features of \ph. Indeed, a conscious effort has been // made here to avoid using several idiosyncratic features of standard // \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.) // // The portions of this program that involve system-dependent code, where // changes might be necessary because of differences between \PASCAL\ compilers // and/or differences between // operating systems, can be identified by looking at the sections whose // numbers are listed under `system dependencies' in the index. Furthermore, // the index entries for `dirty \PASCAL' list all places where the restrictions // of \PASCAL\ have not been followed perfectly, for one reason or another. // \xref[system dependencies] // \xref[dirty \PASCAL] // // 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. // 4. // tangle:pos tex.web:231:3: // The program begins with a normal \PASCAL\ program heading, whose // components will be filled in later, using the conventions of \.[WEB]. // \xref[WEB] // For example, the portion of the program called `\X\glob:Global // variables\X' below will be replaced by a sequence of variable declarations // that starts in $\section\glob$ of this documentation. In this way, we are able // to define each individual global variable when we are prepared to // understand what it means; we do not have to define all of the globals at // once. Cross references in $\section\glob$, where it says ``See also // sections \gglob, \dots,'' also make it possible to look at the set of // all global variables, if desired. Similar remarks apply to the other // portions of the program heading. // // Actually the heading shown here is not quite normal: The |program| line // does not mention any |output| file, because \ph\ would ask the \TeX\ user // to specify a file name if |output| were specified here. // \xref[PASCAL H][\ph] // \xref[system dependencies] // \4 // Compiler directives // $C-,A+,D- // no range check, catch arithmetic overflow, no debug overhead // [$C+,D+] [ ] // but turn everything on when debugging package tex import ( "math" "unsafe" "modernc.org/knuth" ) var ( _ = math.MaxInt32 _ unsafe.Pointer ) type ( char = byte signal int ) func strcopy(dst []char, src string) { for i := 0; i < len(dst) && i < len(src); i++ { dst[i] = src[i] } } func arraystr(a []char) string { b := make([]byte, len(a)) for i, c := range a { b[i] = c } return string(b) } func abs(n int32) int32 { if n >= 0 { return n } return -n } func fabs(f float64) float64 { if f >= 0 { return f } return -f } func round(f float64) int32 { if f >= 0 { return int32(f + 0.5) } return int32(f - 0.5) } // key control points const ( startOfTex = 1 /* go here when \TeX's variables are initialized */ endOfTex = 9998 /* go here to close files and terminate gracefully */ finalEnd = 9999 /* this label marks the ending of the program */ memBot = 0 // smallest index in the |mem| array dumped by \.[INITEX]; // must not be less than |mem_min| fontBase = 0 // smallest internal font number; must not be less // than |min_quarterword| hashSize = 2100 // maximum number of control sequences; it should be at most // about |(mem_max-mem_min)/10| hashPrime = 1777 /* a prime number equal to about 85\pct! of |hash_size| */ hyphSize = 307 // another prime; the number of \.[\\hyphenation] exceptions // \xref[system dependencies] exit = 10 /* go here to leave a procedure */ restart = 20 /* go here to start a procedure again */ reswitch = 21 /* go here to start a case statement again */ continue1 = 22 /* go here to resume a loop */ done = 30 /* go here to exit a loop */ done1 = 31 /* like |done|, when there is more than one loop */ done2 = 32 /* for exiting the second loop in a long block */ done3 = 33 /* for exiting the third loop in a very long block */ done4 = 34 /* for exiting the fourth loop in an extremely long block */ done5 = 35 /* for exiting the fifth loop in an immense block */ done6 = 36 /* for exiting the sixth loop in a block */ found = 40 /* go here when you've found it */ found1 = 41 /* like |found|, when there's more than one per routine */ found2 = 42 /* like |found|, when there's more than two per routine */ notFound = 45 /* go here when you've found nothing */ commonEnding = 50 /* go here when you want to merge with another branch */ empty = 0 /* symbolic name for a null constant */ firstTextChar = 0 /* ordinal number of the smallest element of |text_char| */ lastTextChar = 255 /* ordinal number of the largest element of |text_char| */ nullCode = 0 /* ASCII code that might disappear */ carriageReturn = 015 /* ASCII code used at end of line */ invalidCode = 0177 /* ASCII code that many systems prohibit in text files */ noPrint = 16 /* |selector| setting that makes data disappear */ termOnly = 17 /* printing is destined for the terminal only */ logOnly = 18 /* printing is destined for the transcript file only */ termAndLog = 19 /* normal |selector| setting */ pseudo = 20 /* special |selector| setting for |show_context| */ newString = 21 /* printing is deflected to the string pool */ maxSelector = 21 /* highest selector setting */ batchMode = 0 /* omits all stops and omits terminal output */ nonstopMode = 1 /* omits all stops */ scrollMode = 2 /* omits error stops */ errorStopMode = 3 /* stops at every opportunity to interact */ spotless = 0 /* |history| value when nothing has been amiss yet */ warningIssued = 1 /* |history| value when |begin_diagnostic| has been called */ errorMessageIssued = 2 /* |history| value when |error| has been called */ fatalErrorStop = 3 /* |history| value when termination was premature */ infBad = 10000 /* infinitely bad value */ minQuarterword = 0 /* smallest allowable value in a |quarterword| */ maxQuarterword = 255 /* largest allowable value in a |quarterword| */ hlistNode = 0 /* |type| of hlist nodes */ boxNodeSize = 7 /* number of words to allocate for a box node */ widthOffset = 1 /* position of |width| field in a box node */ depthOffset = 2 /* position of |depth| field in a box node */ heightOffset = 3 /* position of |height| field in a box node */ listOffset = 5 /* position of |list_ptr| field in a box node */ normal = 0 /* the most common case when several cases are named */ stretching = 1 /* glue setting applies to the stretch components */ shrinking = 2 /* glue setting applies to the shrink components */ glueOffset = 6 /* position of |glue_set| in a box node */ vlistNode = 1 /* |type| of vlist nodes */ ruleNode = 2 /* |type| of rule nodes */ ruleNodeSize = 4 /* number of words to allocate for a rule node */ insNode = 3 /* |type| of insertion nodes */ insNodeSize = 5 /* number of words to allocate for an insertion */ markNode = 4 /* |type| of a mark node */ smallNodeSize = 2 /* number of words to allocate for most node types */ adjustNode = 5 /* |type| of an adjust node */ ligatureNode = 6 /* |type| of a ligature node */ discNode = 7 /* |type| of a discretionary node */ whatsitNode = 8 /* |type| of special extension nodes */ mathNode = 9 /* |type| of a math node */ before = 0 /* |subtype| for math node that introduces a formula */ after = 1 /* |subtype| for math node that winds up a formula */ glueNode = 10 /* |type| of node that points to a glue specification */ condMathGlue = 98 /* special |subtype| to suppress glue in the next node */ muGlue = 99 /* |subtype| for math glue */ aLeaders = 100 /* |subtype| for aligned leaders */ cLeaders = 101 /* |subtype| for centered leaders */ xLeaders = 102 /* |subtype| for expanded leaders */ glueSpecSize = 4 /* number of words to allocate for a glue specification */ fil = 1 /* first-order infinity */ fill = 2 /* second-order infinity */ filll = 3 /* third-order infinity */ kernNode = 11 /* |type| of a kern node */ explicit = 1 /* |subtype| of kern nodes from \.[\\kern] and \.[\\/] */ accKern = 2 /* |subtype| of kern nodes from accents */ penaltyNode = 12 /* |type| of a penalty node */ infPenalty = infBad /* ``infinite'' penalty value */ ejectPenalty = -infPenalty /* ``negatively infinite'' penalty value */ unsetNode = 13 /* |type| for an unset node */ hiMemStatUsage = 14 /* the number of one-word nodes always present */ escape = 0 // escape delimiter (called \.\\ in [\sl The \TeX book\/]) // \xref[TeXbook][\sl The \TeX book] relax = 0 /* do nothing ( \.[\\relax] ) */ leftBrace = 1 /* beginning of a group ( \.\[ ) */ rightBrace = 2 /* ending of a group ( \.\] ) */ mathShift = 3 /* mathematics shift character ( \.\$ ) */ tabMark = 4 /* alignment delimiter ( \.\&, \.[\\span] ) */ carRet = 5 /* end of line ( |carriage_return|, \.[\\cr], \.[\\crcr] ) */ outParam = 5 /* output a macro parameter */ macParam = 6 /* macro parameter symbol ( \.\# ) */ supMark = 7 /* superscript ( \.[\char'136] ) */ subMark = 8 /* subscript ( \.[\char'137] ) */ ignore = 9 /* characters to ignore ( \.[\^\^@] ) */ endv = 9 /* end of \ list in alignment template */ spacer = 10 /* characters equivalent to blank space ( \.[\ ] ) */ letter = 11 /* characters regarded as letters ( \.[A..Z], \.[a..z] ) */ otherChar = 12 /* none of the special character types */ activeChar = 13 /* characters that invoke macros ( \.[\char`\~] ) */ parEnd = 13 /* end of paragraph ( \.[\\par] ) */ match = 13 /* match a macro parameter */ comment = 14 /* characters that introduce comments ( \.\% ) */ endMatch = 14 /* end of parameters to macro */ stop = 14 /* end of job ( \.[\\end], \.[\\dump] ) */ invalidChar = 15 /* characters that shouldn't appear ( \.[\^\^?] ) */ delimNum = 15 /* specify delimiter numerically ( \.[\\delimiter] ) */ maxCharCode = 15 /* largest catcode for individual characters */ charNum = 16 /* character specified numerically ( \.[\\char] ) */ mathCharNum = 17 /* explicit math code ( \.[\\mathchar] ) */ mark = 18 /* mark definition ( \.[\\mark] ) */ xray = 19 /* peek inside of \TeX\ ( \.[\\show], \.[\\showbox], etc.~) */ makeBox = 20 /* make a box ( \.[\\box], \.[\\copy], \.[\\hbox], etc.~) */ hmove = 21 /* horizontal motion ( \.[\\moveleft], \.[\\moveright] ) */ vmove = 22 /* vertical motion ( \.[\\raise], \.[\\lower] ) */ unHbox = 23 /* unglue a box ( \.[\\unhbox], \.[\\unhcopy] ) */ unVbox = 24 /* unglue a box ( \.[\\unvbox], \.[\\unvcopy] ) */ removeItem = 25 // nullify last item ( \.[\\unpenalty], // \.[\\unkern], \.[\\unskip] ) hskip = 26 /* horizontal glue ( \.[\\hskip], \.[\\hfil], etc.~) */ vskip = 27 /* vertical glue ( \.[\\vskip], \.[\\vfil], etc.~) */ mskip = 28 /* math glue ( \.[\\mskip] ) */ kern = 29 /* fixed space ( \.[\\kern] ) */ mkern = 30 /* math kern ( \.[\\mkern] ) */ leaderShip = 31 /* use a box ( \.[\\shipout], \.[\\leaders], etc.~) */ halign = 32 /* horizontal table alignment ( \.[\\halign] ) */ valign = 33 /* vertical table alignment ( \.[\\valign] ) */ noAlign = 34 /* temporary escape from alignment ( \.[\\noalign] ) */ vrule = 35 /* vertical rule ( \.[\\vrule] ) */ hrule = 36 /* horizontal rule ( \.[\\hrule] ) */ insert = 37 /* vlist inserted in box ( \.[\\insert] ) */ vadjust = 38 /* vlist inserted in enclosing paragraph ( \.[\\vadjust] ) */ ignoreSpaces = 39 /* gobble |spacer| tokens ( \.[\\ignorespaces] ) */ afterAssignment = 40 /* save till assignment is done ( \.[\\afterassignment] ) */ afterGroup = 41 /* save till group is done ( \.[\\aftergroup] ) */ breakPenalty = 42 /* additional badness ( \.[\\penalty] ) */ startPar = 43 /* begin paragraph ( \.[\\indent], \.[\\noindent] ) */ italCorr = 44 /* italic correction ( \.[\\/] ) */ accent = 45 /* attach accent in text ( \.[\\accent] ) */ mathAccent = 46 /* attach accent in math ( \.[\\mathaccent] ) */ discretionary = 47 /* discretionary texts ( \.[\\-], \.[\\discretionary] ) */ eqNo = 48 /* equation number ( \.[\\eqno], \.[\\leqno] ) */ leftRight = 49 /* variable delimiter ( \.[\\left], \.[\\right] ) */ mathComp = 50 /* component of formula ( \.[\\mathbin], etc.~) */ limitSwitch = 51 /* diddle limit conventions ( \.[\\displaylimits], etc.~) */ above = 52 /* generalized fraction ( \.[\\above], \.[\\atop], etc.~) */ mathStyle = 53 /* style specification ( \.[\\displaystyle], etc.~) */ mathChoice = 54 /* choice specification ( \.[\\mathchoice] ) */ nonScript = 55 /* conditional math glue ( \.[\\nonscript] ) */ vcenter = 56 /* vertically center a vbox ( \.[\\vcenter] ) */ caseShift = 57 /* force specific case ( \.[\\lowercase], \.[\\uppercase]~) */ message = 58 /* send to user ( \.[\\message], \.[\\errmessage] ) */ extension = 59 /* extensions to \TeX\ ( \.[\\write], \.[\\special], etc.~) */ inStream = 60 /* files for reading ( \.[\\openin], \.[\\closein] ) */ beginGroup = 61 /* begin local grouping ( \.[\\begingroup] ) */ endGroup = 62 /* end local grouping ( \.[\\endgroup] ) */ omit = 63 /* omit alignment template ( \.[\\omit] ) */ exSpace = 64 /* explicit space ( \.[\\\ ] ) */ noBoundary = 65 /* suppress boundary ligatures ( \.[\\noboundary] ) */ radical = 66 /* square root and similar signs ( \.[\\radical] ) */ endCsName = 67 /* end control sequence ( \.[\\endcsname] ) */ minInternal = 68 /* the smallest code that can follow \.[\\the] */ charGiven = 68 /* character code defined by \.[\\chardef] */ mathGiven = 69 /* math code defined by \.[\\mathchardef] */ lastItem = 70 // most recent item ( \.[\\lastpenalty], // \.[\\lastkern], \.[\\lastskip] ) maxNonPrefixedCommand = 70 /* largest command code that can't be \.[\\global] */ toksRegister = 71 /* token list register ( \.[\\toks] ) */ assignToks = 72 /* special token list ( \.[\\output], \.[\\everypar], etc.~) */ assignInt = 73 /* user-defined integer ( \.[\\tolerance], \.[\\day], etc.~) */ assignDimen = 74 /* user-defined length ( \.[\\hsize], etc.~) */ assignGlue = 75 /* user-defined glue ( \.[\\baselineskip], etc.~) */ assignMuGlue = 76 /* user-defined muglue ( \.[\\thinmuskip], etc.~) */ assignFontDimen = 77 /* user-defined font dimension ( \.[\\fontdimen] ) */ assignFontInt = 78 // user-defined font integer ( \.[\\hyphenchar], // \.[\\skewchar] ) setAux = 79 /* specify state info ( \.[\\spacefactor], \.[\\prevdepth] ) */ setPrevGraf = 80 /* specify state info ( \.[\\prevgraf] ) */ setPageDimen = 81 /* specify state info ( \.[\\pagegoal], etc.~) */ setPageInt = 82 // specify state info ( \.[\\deadcycles], // \.[\\insertpenalties] ) setBoxDimen = 83 /* change dimension of box ( \.[\\wd], \.[\\ht], \.[\\dp] ) */ setShape = 84 /* specify fancy paragraph shape ( \.[\\parshape] ) */ defCode = 85 /* define a character code ( \.[\\catcode], etc.~) */ defFamily = 86 /* declare math fonts ( \.[\\textfont], etc.~) */ setFont = 87 /* set current font ( font identifiers ) */ defFont = 88 /* define a font file ( \.[\\font] ) */ register = 89 /* internal register ( \.[\\count], \.[\\dimen], etc.~) */ maxInternal = 89 /* the largest code that can follow \.[\\the] */ advance = 90 /* advance a register or parameter ( \.[\\advance] ) */ multiply = 91 /* multiply a register or parameter ( \.[\\multiply] ) */ divide = 92 /* divide a register or parameter ( \.[\\divide] ) */ prefix = 93 /* qualify a definition ( \.[\\global], \.[\\long], \.[\\outer] ) */ let = 94 /* assign a command code ( \.[\\let], \.[\\futurelet] ) */ shorthandDef = 95 /* code definition ( \.[\\chardef], \.[\\countdef], etc.~) */ readToCs = 96 /* read into a control sequence ( \.[\\read] ) */ def = 97 /* macro definition ( \.[\\def], \.[\\gdef], \.[\\xdef], \.[\\edef] ) */ setBox = 98 /* set a box ( \.[\\setbox] ) */ hyphData = 99 /* hyphenation data ( \.[\\hyphenation], \.[\\patterns] ) */ setInteraction = 100 /* define level of interaction ( \.[\\batchmode], etc.~) */ maxCommand = 100 /* the largest command code seen at |big_switch| */ undefinedCs = maxCommand + 1 /* initial state of most |eq_type| fields */ expandAfter = maxCommand + 2 /* special expansion ( \.[\\expandafter] ) */ noExpand = maxCommand + 3 /* special nonexpansion ( \.[\\noexpand] ) */ input = maxCommand + 4 /* input a source file ( \.[\\input], \.[\\endinput] ) */ ifTest = maxCommand + 5 /* conditional text ( \.[\\if], \.[\\ifcase], etc.~) */ fiOrElse = maxCommand + 6 /* delimiters for conditionals ( \.[\\else], etc.~) */ csName = maxCommand + 7 /* make a control sequence from tokens ( \.[\\csname] ) */ convert = maxCommand + 8 /* convert to text ( \.[\\number], \.[\\string], etc.~) */ the = maxCommand + 9 /* expand an internal quantity ( \.[\\the] ) */ topBotMark = maxCommand + 10 /* inserted mark ( \.[\\topmark], etc.~) */ call = maxCommand + 11 /* non-long, non-outer control sequence */ longCall = maxCommand + 12 /* long, non-outer control sequence */ outerCall = maxCommand + 13 /* non-long, outer control sequence */ longOuterCall = maxCommand + 14 /* long, outer control sequence */ endTemplate = maxCommand + 15 /* end of an alignment template */ dontExpand = maxCommand + 16 /* the following token was marked by \.[\\noexpand] */ glueRef = maxCommand + 17 /* the equivalent points to a glue specification */ shapeRef = maxCommand + 18 /* the equivalent points to a parshape specification */ boxRef = maxCommand + 19 /* the equivalent points to a box node, or is |null| */ data = maxCommand + 20 /* the equivalent is simply a halfword number */ vmode = 1 /* vertical mode */ hmode = vmode + maxCommand + 1 /* horizontal mode */ mmode = hmode + maxCommand + 1 /* math mode */ levelZero = minQuarterword /* level for undefined quantities */ levelOne = levelZero + 1 /* outermost level for defined quantities */ activeBase = 1 /* beginning of region 1, for active character equivalents */ singleBase = activeBase + 256 /* equivalents of one-character control sequences */ nullCs = singleBase + 256 /* equivalent of \.[\\csname\\endcsname] */ hashBase = nullCs + 1 /* beginning of region 2, for the hash table */ frozenControlSequence = hashBase + hashSize /* for error recovery */ frozenProtection = frozenControlSequence /* inaccessible but definable */ frozenCr = frozenControlSequence + 1 /* permanent `\.[\\cr]' */ frozenEndGroup = frozenControlSequence + 2 /* permanent `\.[\\endgroup]' */ frozenRight = frozenControlSequence + 3 /* permanent `\.[\\right]' */ frozenFi = frozenControlSequence + 4 /* permanent `\.[\\fi]' */ frozenEndTemplate = frozenControlSequence + 5 /* permanent `\.[\\endtemplate]' */ frozenEndv = frozenControlSequence + 6 /* second permanent `\.[\\endtemplate]' */ frozenRelax = frozenControlSequence + 7 /* permanent `\.[\\relax]' */ endWrite = frozenControlSequence + 8 /* permanent `\.[\\endwrite]' */ frozenDontExpand = frozenControlSequence + 9 /* permanent `\.[\\notexpanded:]' */ frozenNullFont = frozenControlSequence + 10 /* permanent `\.[\\nullfont]' */ fontIdBase = frozenNullFont - fontBase /* begins table of 257 permanent font identifiers */ undefinedControlSequence = frozenNullFont + 257 /* dummy location */ glueBase = undefinedControlSequence + 1 /* beginning of region 3 */ lineSkipCode = 0 /* interline glue if |baseline_skip| is infeasible */ baselineSkipCode = 1 /* desired glue between baselines */ parSkipCode = 2 /* extra glue just above a paragraph */ aboveDisplaySkipCode = 3 /* extra glue just above displayed math */ belowDisplaySkipCode = 4 /* extra glue just below displayed math */ aboveDisplayShortSkipCode = 5 /* glue above displayed math following short lines */ belowDisplayShortSkipCode = 6 /* glue below displayed math following short lines */ leftSkipCode = 7 /* glue at left of justified lines */ rightSkipCode = 8 /* glue at right of justified lines */ topSkipCode = 9 /* glue at top of main pages */ splitTopSkipCode = 10 /* glue at top of split pages */ tabSkipCode = 11 /* glue between aligned entries */ spaceSkipCode = 12 /* glue between words (if not |zero_glue|) */ xspaceSkipCode = 13 /* glue after sentences (if not |zero_glue|) */ parFillSkipCode = 14 /* glue on last line of paragraph */ thinMuSkipCode = 15 /* thin space in math formula */ medMuSkipCode = 16 /* medium space in math formula */ thickMuSkipCode = 17 /* thick space in math formula */ gluePars = 18 /* total number of glue parameters */ skipBase = glueBase + gluePars /* table of 256 ``skip'' registers */ muSkipBase = skipBase + 256 /* table of 256 ``muskip'' registers */ localBase = muSkipBase + 256 /* beginning of region 4 */ parShapeLoc = localBase /* specifies paragraph shape */ outputRoutineLoc = localBase + 1 /* points to token list for \.[\\output] */ everyParLoc = localBase + 2 /* points to token list for \.[\\everypar] */ everyMathLoc = localBase + 3 /* points to token list for \.[\\everymath] */ everyDisplayLoc = localBase + 4 /* points to token list for \.[\\everydisplay] */ everyHboxLoc = localBase + 5 /* points to token list for \.[\\everyhbox] */ everyVboxLoc = localBase + 6 /* points to token list for \.[\\everyvbox] */ everyJobLoc = localBase + 7 /* points to token list for \.[\\everyjob] */ everyCrLoc = localBase + 8 /* points to token list for \.[\\everycr] */ errHelpLoc = localBase + 9 /* points to token list for \.[\\errhelp] */ toksBase = localBase + 10 /* table of 256 token list registers */ boxBase = toksBase + 256 /* table of 256 box registers */ curFontLoc = boxBase + 256 /* internal font number outside math mode */ mathFontBase = curFontLoc + 1 /* table of 48 math font numbers */ catCodeBase = mathFontBase + 48 /* table of 256 command codes (the ``catcodes'') */ lcCodeBase = catCodeBase + 256 /* table of 256 lowercase mappings */ ucCodeBase = lcCodeBase + 256 /* table of 256 uppercase mappings */ sfCodeBase = ucCodeBase + 256 /* table of 256 spacefactor mappings */ mathCodeBase = sfCodeBase + 256 /* table of 256 math mode mappings */ intBase = mathCodeBase + 256 /* beginning of region 5 */ pretoleranceCode = 0 /* badness tolerance before hyphenation */ toleranceCode = 1 /* badness tolerance after hyphenation */ linePenaltyCode = 2 /* added to the badness of every line */ hyphenPenaltyCode = 3 /* penalty for break after discretionary hyphen */ exHyphenPenaltyCode = 4 /* penalty for break after explicit hyphen */ clubPenaltyCode = 5 /* penalty for creating a club line */ widowPenaltyCode = 6 /* penalty for creating a widow line */ displayWidowPenaltyCode = 7 /* ditto, just before a display */ brokenPenaltyCode = 8 /* penalty for breaking a page at a broken line */ binOpPenaltyCode = 9 /* penalty for breaking after a binary operation */ relPenaltyCode = 10 /* penalty for breaking after a relation */ preDisplayPenaltyCode = 11 /* penalty for breaking just before a displayed formula */ postDisplayPenaltyCode = 12 /* penalty for breaking just after a displayed formula */ interLinePenaltyCode = 13 /* additional penalty between lines */ doubleHyphenDemeritsCode = 14 /* demerits for double hyphen break */ finalHyphenDemeritsCode = 15 /* demerits for final hyphen break */ adjDemeritsCode = 16 /* demerits for adjacent incompatible lines */ magCode = 17 /* magnification ratio */ delimiterFactorCode = 18 /* ratio for variable-size delimiters */ loosenessCode = 19 /* change in number of lines for a paragraph */ timeCode = 20 /* current time of day */ dayCode = 21 /* current day of the month */ monthCode = 22 /* current month of the year */ yearCode = 23 /* current year of our Lord */ showBoxBreadthCode = 24 /* nodes per level in |show_box| */ showBoxDepthCode = 25 /* maximum level in |show_box| */ hbadnessCode = 26 /* hboxes exceeding this badness will be shown by |hpack| */ vbadnessCode = 27 /* vboxes exceeding this badness will be shown by |vpack| */ pausingCode = 28 /* pause after each line is read from a file */ tracingOnlineCode = 29 /* show diagnostic output on terminal */ tracingMacrosCode = 30 /* show macros as they are being expanded */ tracingStatsCode = 31 /* show memory usage if \TeX\ knows it */ tracingParagraphsCode = 32 /* show line-break calculations */ tracingPagesCode = 33 /* show page-break calculations */ tracingOutputCode = 34 /* show boxes when they are shipped out */ tracingLostCharsCode = 35 /* show characters that aren't in the font */ tracingCommandsCode = 36 /* show command codes at |big_switch| */ tracingRestoresCode = 37 /* show equivalents when they are restored */ ucHyphCode = 38 /* hyphenate words beginning with a capital letter */ outputPenaltyCode = 39 /* penalty found at current page break */ maxDeadCyclesCode = 40 /* bound on consecutive dead cycles of output */ hangAfterCode = 41 /* hanging indentation changes after this many lines */ floatingPenaltyCode = 42 /* penalty for insertions held over after a split */ globalDefsCode = 43 /* override \.[\\global] specifications */ curFamCode = 44 /* current family */ escapeCharCode = 45 /* escape character for token output */ defaultHyphenCharCode = 46 /* value of \.[\\hyphenchar] when a font is loaded */ defaultSkewCharCode = 47 /* value of \.[\\skewchar] when a font is loaded */ endLineCharCode = 48 /* character placed at the right end of the buffer */ newLineCharCode = 49 /* character that prints as |print_ln| */ languageCode = 50 /* current hyphenation table */ leftHyphenMinCode = 51 /* minimum left hyphenation fragment size */ rightHyphenMinCode = 52 /* minimum right hyphenation fragment size */ holdingInsertsCode = 53 /* do not remove insertion nodes from \.[\\box255] */ errorContextLinesCode = 54 /* maximum intermediate line pairs shown */ intPars = 55 /* total number of integer parameters */ countBase = intBase + intPars /* 256 user \.[\\count] registers */ delCodeBase = countBase + 256 /* 256 delimiter code mappings */ dimenBase = delCodeBase + 256 /* beginning of region 6 */ parIndentCode = 0 /* indentation of paragraphs */ mathSurroundCode = 1 /* space around math in text */ lineSkipLimitCode = 2 /* threshold for |line_skip| instead of |baseline_skip| */ hsizeCode = 3 /* line width in horizontal mode */ vsizeCode = 4 /* page height in vertical mode */ maxDepthCode = 5 /* maximum depth of boxes on main pages */ splitMaxDepthCode = 6 /* maximum depth of boxes on split pages */ boxMaxDepthCode = 7 /* maximum depth of explicit vboxes */ hfuzzCode = 8 /* tolerance for overfull hbox messages */ vfuzzCode = 9 /* tolerance for overfull vbox messages */ delimiterShortfallCode = 10 /* maximum amount uncovered by variable delimiters */ nullDelimiterSpaceCode = 11 /* blank space in null delimiters */ scriptSpaceCode = 12 /* extra space after subscript or superscript */ preDisplaySizeCode = 13 /* length of text preceding a display */ displayWidthCode = 14 /* length of line for displayed equation */ displayIndentCode = 15 /* indentation of line for displayed equation */ overfullRuleCode = 16 /* width of rule that identifies overfull hboxes */ hangIndentCode = 17 /* amount of hanging indentation */ hOffsetCode = 18 /* amount of horizontal offset when shipping pages out */ vOffsetCode = 19 /* amount of vertical offset when shipping pages out */ emergencyStretchCode = 20 /* reduces badnesses on final pass of line-breaking */ dimenPars = 21 /* total number of dimension parameters */ scaledBase = dimenBase + dimenPars /* table of 256 user-defined \.[\\dimen] registers */ eqtbSize = scaledBase + 255 /* largest subscript of |eqtb| */ restoreOldValue = 0 /* |save_type| when a value should be restored later */ restoreZero = 1 /* |save_type| when an undefined entry should be restored */ insertToken = 2 /* |save_type| when a token is being saved for later use */ levelBoundary = 3 /* |save_type| corresponding to beginning of group */ bottomLevel = 0 /* group code for the outside world */ simpleGroup = 1 /* group code for local structure only */ hboxGroup = 2 /* code for `\.[\\hbox]\grp' */ adjustedHboxGroup = 3 /* code for `\.[\\hbox]\grp' in vertical mode */ vboxGroup = 4 /* code for `\.[\\vbox]\grp' */ vtopGroup = 5 /* code for `\.[\\vtop]\grp' */ alignGroup = 6 /* code for `\.[\\halign]\grp', `\.[\\valign]\grp' */ noAlignGroup = 7 /* code for `\.[\\noalign]\grp' */ outputGroup = 8 /* code for output routine */ mathGroup = 9 /* code for, e.g., `\.[\char'136]\grp' */ discGroup = 10 /* code for `\.[\\discretionary]\grp\grp\grp' */ insertGroup = 11 /* code for `\.[\\insert]\grp', `\.[\\vadjust]\grp' */ vcenterGroup = 12 /* code for `\.[\\vcenter]\grp' */ mathChoiceGroup = 13 /* code for `\.[\\mathchoice]\grp\grp\grp\grp' */ semiSimpleGroup = 14 /* code for `\.[\\begingroup...\\endgroup]' */ mathShiftGroup = 15 /* code for `\.[\$...\$]' */ mathLeftGroup = 16 /* code for `\.[\\left...\\right]' */ maxGroupCode = 16 leftBraceToken = 0400 /* $2^8\cdot|left_brace|$ */ leftBraceLimit = 01000 /* $2^8\cdot(|left_brace|+1)$ */ rightBraceToken = 01000 /* $2^8\cdot|right_brace|$ */ rightBraceLimit = 01400 /* $2^8\cdot(|right_brace|+1)$ */ mathShiftToken = 01400 /* $2^8\cdot|math_shift|$ */ tabToken = 02000 /* $2^8\cdot|tab_mark|$ */ outParamToken = 02400 /* $2^8\cdot|out_param|$ */ spaceToken = 05040 /* $2^8\cdot|spacer|+|" "|$ */ letterToken = 05400 /* $2^8\cdot|letter|$ */ otherToken = 06000 /* $2^8\cdot|other_char|$ */ matchToken = 06400 /* $2^8\cdot|match|$ */ endMatchToken = 07000 /* $2^8\cdot|end_match|$ */ midLine = 1 /* |state| code when scanning a line of characters */ skipBlanks = 2 + maxCharCode /* |state| code when ignoring blanks */ newLine = 3 + maxCharCode + maxCharCode /* |state| code at start of line */ skipping = 1 /* |scanner_status| when passing conditional text */ defining = 2 /* |scanner_status| when reading a macro definition */ matching = 3 /* |scanner_status| when reading macro arguments */ aligning = 4 /* |scanner_status| when reading an alignment preamble */ absorbing = 5 /* |scanner_status| when reading a balanced text */ tokenList = 0 /* |state| code when scanning a token list */ parameter = 0 /* |token_type| code for parameter */ uTemplate = 1 /* |token_type| code for \ template */ vTemplate = 2 /* |token_type| code for \ template */ backedUp = 3 /* |token_type| code for text to be reread */ inserted = 4 /* |token_type| code for inserted texts */ macro = 5 /* |token_type| code for defined control sequences */ outputText = 6 /* |token_type| code for output routines */ everyParText = 7 /* |token_type| code for \.[\\everypar] */ everyMathText = 8 /* |token_type| code for \.[\\everymath] */ everyDisplayText = 9 /* |token_type| code for \.[\\everydisplay] */ everyHboxText = 10 /* |token_type| code for \.[\\everyhbox] */ everyVboxText = 11 /* |token_type| code for \.[\\everyvbox] */ everyJobText = 12 /* |token_type| code for \.[\\everyjob] */ everyCrText = 13 /* |token_type| code for \.[\\everycr] */ markText = 14 /* |token_type| code for \.[\\topmark], etc. */ writeText = 15 /* |token_type| code for \.[\\write] */ switch1 = 25 /* a label in |get_next| */ startCs = 26 /* another */ noExpandFlag = 257 /* this characterizes a special variant of |relax| */ topMarkCode = 0 /* the mark in effect at the previous page break */ firstMarkCode = 1 /* the first mark between |top_mark| and |bot_mark| */ botMarkCode = 2 /* the mark in effect at the current page break */ splitFirstMarkCode = 3 /* the first mark found by \.[\\vsplit] */ splitBotMarkCode = 4 /* the last mark found by \.[\\vsplit] */ intVal = 0 /* integer values */ dimenVal = 1 /* dimension values */ glueVal = 2 /* glue specifications */ muVal = 3 /* math glue specifications */ identVal = 4 /* font identifier */ tokVal = 5 /* token lists */ inputLineNoCode = glueVal + 1 /* code for \.[\\inputlineno] */ badnessCode = glueVal + 2 /* code for \.[\\badness] */ octalToken = otherToken + '\'' /* apostrophe, indicates an octal constant */ hexToken = otherToken + '"' /* double quote, indicates a hex constant */ alphaToken = otherToken + '`' /* reverse apostrophe, precedes alpha constants */ pointToken = otherToken + '.' /* decimal point */ continentalPointToken = otherToken + ',' /* decimal point, Eurostyle */ zeroToken = otherToken + '0' /* zero, the smallest digit */ aToken = letterToken + 'A' /* the smallest special hex digit */ otherAToken = otherToken + 'A' /* special hex digit of type |other_char| */ attachFraction = 88 /* go here to pack |cur_val| and |f| into |cur_val| */ attachSign = 89 /* go here when |cur_val| is correct except perhaps for sign */ defaultRule = 26214 /* 0.4\thinspace pt */ numberCode = 0 /* command code for \.[\\number] */ romanNumeralCode = 1 /* command code for \.[\\romannumeral] */ stringCode = 2 /* command code for \.[\\string] */ meaningCode = 3 /* command code for \.[\\meaning] */ fontNameCode = 4 /* command code for \.[\\fontname] */ jobNameCode = 5 /* command code for \.[\\jobname] */ closed = 2 /* not open, or at end of file */ justOpen = 1 /* newly opened, first line not yet read */ ifCharCode = 0 /* `\.[\\if]' */ ifCatCode = 1 /* `\.[\\ifcat]' */ ifIntCode = 2 /* `\.[\\ifnum]' */ ifDimCode = 3 /* `\.[\\ifdim]' */ ifOddCode = 4 /* `\.[\\ifodd]' */ ifVmodeCode = 5 /* `\.[\\ifvmode]' */ ifHmodeCode = 6 /* `\.[\\ifhmode]' */ ifMmodeCode = 7 /* `\.[\\ifmmode]' */ ifInnerCode = 8 /* `\.[\\ifinner]' */ ifVoidCode = 9 /* `\.[\\ifvoid]' */ ifHboxCode = 10 /* `\.[\\ifhbox]' */ ifVboxCode = 11 /* `\.[\\ifvbox]' */ ifxCode = 12 /* `\.[\\ifx]' */ ifEofCode = 13 /* `\.[\\ifeof]' */ ifTrueCode = 14 /* `\.[\\iftrue]' */ ifFalseCode = 15 /* `\.[\\iffalse]' */ ifCaseCode = 16 /* `\.[\\ifcase]' */ ifNodeSize = 2 /* number of words in stack entry for conditionals */ ifCode = 1 /* code for \.[\\if...] being evaluated */ fiCode = 2 /* code for \.[\\fi] */ elseCode = 3 /* code for \.[\\else] */ orCode = 4 /* code for \.[\\or] */ formatDefaultLength = 20 /* length of the |TEX_format_default| string */ formatAreaLength = 11 /* length of its area part */ formatExtLength = 4 /* length of its `\.[.fmt]' part */ formatExtension = /* ".fmt" */ 786 /* the extension, as a \.[WEB] constant */ noTag = 0 /* vanilla character */ ligTag = 1 /* character has a ligature/kerning program */ listTag = 2 /* character has a successor in a charlist */ extTag = 3 /* character is extensible */ slantCode = 1 spaceCode = 2 spaceStretchCode = 3 spaceShrinkCode = 4 xHeightCode = 5 quadCode = 6 extraSpaceCode = 7 nonAddress = 0 /* a spurious |bchar_label| */ badTfm = 11 /* label for |read_font_info| */ setChar0 = 0 /* typeset character 0 and move right */ set1 = 128 /* typeset a character and move right */ setRule = 132 /* typeset a rule and move right */ putRule = 137 /* typeset a rule */ nop = 138 /* no operation */ bop = 139 /* beginning of page */ eop = 140 /* ending of page */ push = 141 /* save the current positions */ pop = 142 /* restore previous positions */ right1 = 143 /* move right */ w0 = 147 /* move right by |w| */ w1 = 148 /* move right and set |w| */ x0 = 152 /* move right by |x| */ x1 = 153 /* move right and set |x| */ down1 = 157 /* move down */ y0 = 161 /* move down by |y| */ y1 = 162 /* move down and set |y| */ z0 = 166 /* move down by |z| */ z1 = 167 /* move down and set |z| */ fntNum0 = 171 /* set current font to 0 */ fnt1 = 235 /* set current font */ xxx1 = 239 /* extension to \.[DVI] primitives */ xxx4 = 242 /* potentially long extension to \.[DVI] primitives */ fntDef1 = 243 /* define the meaning of a font number */ pre = 247 /* preamble */ post = 248 /* postamble beginning */ postPost = 249 /* postamble ending */ idByte = 2 /* identifies the kind of \.[DVI] files described here */ movementNodeSize = 3 /* number of words per entry in the down and right stacks */ yHere = 1 /* |info| when the movement entry points to a |y| command */ zHere = 2 /* |info| when the movement entry points to a |z| command */ yzOk = 3 /* |info| corresponding to an unconstrained \\[down] command */ yOk = 4 /* |info| corresponding to a \\[down] that can't become a |z| */ zOk = 5 /* |info| corresponding to a \\[down] that can't become a |y| */ dFixed = 6 /* |info| corresponding to a \\[down] that can't change */ noneSeen = 0 /* no |y_here| or |z_here| nodes have been encountered yet */ ySeen = 6 /* we have seen |y_here| but not |z_here| */ zSeen = 12 /* we have seen |z_here| but not |y_here| */ movePast = 13 /* go to this label when advancing past glue or a rule */ finRule = 14 /* go to this label to finish processing a rule */ nextP = 15 /* go to this label when finished with node |p| */ exactly = 0 /* a box dimension is pre-specified */ additional = 1 /* a box dimension is increased from the natural one */ noadSize = 4 /* number of words in a normal noad */ mathChar = 1 /* |math_type| when the attribute is simple */ subBox = 2 /* |math_type| when the attribute is a box */ subMlist = 3 /* |math_type| when the attribute is a formula */ mathTextChar = 4 /* |math_type| when italic correction is dubious */ ordNoad = unsetNode + 3 /* |type| of a noad classified Ord */ opNoad = ordNoad + 1 /* |type| of a noad classified Op */ binNoad = ordNoad + 2 /* |type| of a noad classified Bin */ relNoad = ordNoad + 3 /* |type| of a noad classified Rel */ openNoad = ordNoad + 4 /* |type| of a noad classified Open */ closeNoad = ordNoad + 5 /* |type| of a noad classified Close */ punctNoad = ordNoad + 6 /* |type| of a noad classified Punct */ innerNoad = ordNoad + 7 /* |type| of a noad classified Inner */ limits = 1 /* |subtype| of |op_noad| whose scripts are to be above, below */ noLimits = 2 /* |subtype| of |op_noad| whose scripts are to be normal */ radicalNoad = innerNoad + 1 /* |type| of a noad for square roots */ radicalNoadSize = 5 /* number of |mem| words in a radical noad */ fractionNoad = radicalNoad + 1 /* |type| of a noad for generalized fractions */ fractionNoadSize = 6 /* number of |mem| words in a fraction noad */ underNoad = fractionNoad + 1 /* |type| of a noad for underlining */ overNoad = underNoad + 1 /* |type| of a noad for overlining */ accentNoad = overNoad + 1 /* |type| of a noad for accented subformulas */ accentNoadSize = 5 /* number of |mem| words in an accent noad */ vcenterNoad = accentNoad + 1 /* |type| of a noad for \.[\\vcenter] */ leftNoad = vcenterNoad + 1 /* |type| of a noad for \.[\\left] */ rightNoad = leftNoad + 1 /* |type| of a noad for \.[\\right] */ styleNode = unsetNode + 1 /* |type| of a style node */ styleNodeSize = 3 /* number of words in a style node */ displayStyle = 0 /* |subtype| for \.[\\displaystyle] */ textStyle = 2 /* |subtype| for \.[\\textstyle] */ scriptStyle = 4 /* |subtype| for \.[\\scriptstyle] */ scriptScriptStyle = 6 /* |subtype| for \.[\\scriptscriptstyle] */ cramped = 1 /* add this to an uncramped style if you want to cramp it */ choiceNode = unsetNode + 2 /* |type| of a choice node */ textSize = 0 /* size code for the largest size in a family */ scriptSize = 16 /* size code for the medium size in a family */ scriptScriptSize = 32 /* size code for the smallest size in a family */ totalMathsyParams = 22 totalMathexParams = 13 doneWithNoad = 80 /* go here when a noad has been fully translated */ doneWithNode = 81 /* go here when a node has been fully converted */ checkDimensions = 82 /* go here to update |max_h| and |max_d| */ deleteQ = 83 /* go here to delete |q| and move to the next node */ mathSpacing = // \hskip-35pt /* "0234000122*4000133**3**344*0400400*000000234000111*1111112341011" */ 892 /* $ \hskip-35pt$ */ alignStackNodeSize = 5 /* number of |mem| words to save alignment states */ spanCode = 256 /* distinct from any character */ crCode = 257 /* distinct from |span_code| and from any character */ crCrCode = crCode + 1 /* this distinguishes \.[\\crcr] from \.[\\cr] */ spanNodeSize = 2 /* number of |mem| words for a span node */ tightFit = 3 // fitness classification for lines shrinking 0.5 to 1.0 of their // shrinkability looseFit = 1 // fitness classification for lines stretching 0.5 to 1.0 of their // stretchability veryLooseFit = 0 // fitness classification for lines stretching more than // their stretchability decentFit = 2 /* fitness classification for all other lines */ activeNodeSize = 3 /* number of words in active nodes */ unhyphenated = 0 /* the |type| of a normal active break node */ hyphenated = 1 /* the |type| of an active node that breaks at a |disc_node| */ passiveNodeSize = 2 /* number of words in passive nodes */ deltaNodeSize = 7 /* number of words in a delta node */ deltaNode = 2 /* |type| field in a delta node */ deactivate = 60 /* go here when node |r| should be deactivated */ updateHeights = 90 /* go here to record glue in the |active_height| table */ insertsOnly = 1 /* |page_contents| when an insert node has been contributed, but no boxes */ boxThere = 2 /* |page_contents| when a box or rule has been contributed */ pageInsNodeSize = 4 /* number of words for a page insertion node */ inserting = 0 /* an insertion class that has not yet overflowed */ splitUp = 1 /* an overflowed insertion class */ contribute = 80 /* go here to link a node into the current page */ bigSwitch = 60 /* go here to branch on the next token of input */ mainLoop = 70 /* go here to typeset a string of consecutive characters */ mainLoopWrapup = 80 /* go here to finish a character or ligature */ mainLoopMove = 90 /* go here to advance the ligature cursor */ mainLoopMoveLig = 95 /* same, when advancing past a generated ligature */ mainLoopLookahead = 100 /* go here to bring in another character, if any */ mainLigLoop = 110 /* go here to check for ligatures or kerning */ appendNormalSpace = 120 /* go here to append a normal space between words */ filCode = 0 /* identifies \.[\\hfil] and \.[\\vfil] */ fillCode = 1 /* identifies \.[\\hfill] and \.[\\vfill] */ ssCode = 2 /* identifies \.[\\hss] and \.[\\vss] */ filNegCode = 3 /* identifies \.[\\hfilneg] and \.[\\vfilneg] */ skipCode = 4 /* identifies \.[\\hskip] and \.[\\vskip] */ mskipCode = 5 /* identifies \.[\\mskip] */ boxCode = 0 /* |chr_code| for `\.[\\box]' */ copyCode = 1 /* |chr_code| for `\.[\\copy]' */ lastBoxCode = 2 /* |chr_code| for `\.[\\lastbox]' */ vsplitCode = 3 /* |chr_code| for `\.[\\vsplit]' */ vtopCode = 4 /* |chr_code| for `\.[\\vtop]' */ aboveCode = 0 /* `\.[\\above]' */ overCode = 1 /* `\.[\\over]' */ atopCode = 2 /* `\.[\\atop]' */ delimitedCode = 3 /* `\.[\\abovewithdelims]', etc. */ charDefCode = 0 /* |shorthand_def| for \.[\\chardef] */ mathCharDefCode = 1 /* |shorthand_def| for \.[\\mathchardef] */ countDefCode = 2 /* |shorthand_def| for \.[\\countdef] */ dimenDefCode = 3 /* |shorthand_def| for \.[\\dimendef] */ skipDefCode = 4 /* |shorthand_def| for \.[\\skipdef] */ muSkipDefCode = 5 /* |shorthand_def| for \.[\\muskipdef] */ toksDefCode = 6 /* |shorthand_def| for \.[\\toksdef] */ showCode = 0 /* \.[\\show] */ showBoxCode = 1 /* \.[\\showbox] */ showTheCode = 2 /* \.[\\showthe] */ showListsCode = 3 /* \.[\\showlists] */ badFmt = 6666 /* go here if the format file is unacceptable */ breakpoint = 888 /* place where a breakpoint is desirable */ writeNodeSize = 2 /* number of words in a write/whatsit node */ openNodeSize = 3 /* number of words in an open/whatsit node */ openNode = 0 /* |subtype| in whatsits that represent files to \.[\\openout] */ writeNode = 1 /* |subtype| in whatsits that represent things to \.[\\write] */ closeNode = 2 /* |subtype| in whatsits that represent streams to \.[\\closeout] */ specialNode = 3 /* |subtype| in whatsits that represent \.[\\special] things */ languageNode = 4 /* |subtype| in whatsits that change the current language */ immediateCode = 4 /* command modifier for \.[\\immediate] */ setLanguageCode = 5 /* command modifier for \.[\\setlanguage] */ // Constants in the outer block memMax = 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| memMin = 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| bufSize = 500 // maximum number of characters simultaneously present in // current lines of open files and in control sequences between // \.[\\csname] and \.[\\endcsname]; must not exceed |max_halfword| errorLine = 72 // width of context lines on terminal error messages halfErrorLine = 42 // width of first lines of contexts in terminal // error messages; should be between 30 and |error_line-15| maxPrintLine = 79 // width of longest text lines output; should be at least 60 stackSize = 200 // maximum number of simultaneous input sources maxInOpen = 6 // maximum number of input files and error insertions that // can be going on simultaneously fontMax = 75 // maximum internal font number; must not exceed |max_quarterword| // and must be at most |font_base+256| fontMemSize = 20000 // number of words of |font_info| for all fonts paramSize = 60 // maximum number of simultaneous macro parameters nestSize = 40 // maximum number of semantic levels simultaneously active maxStrings = 3000 // maximum number of strings; must not exceed |max_halfword| stringVacancies = 8000 // the minimum number of characters that should be // available for the user's control sequences and font names, // after \TeX's own error messages are stored poolSize = 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 saveSize = 600 // space for saving values outside of current group; must be // at most |max_halfword| trieSize = 8000 // space for hyphenation patterns; should be larger for // \.[INITEX] than it is in production versions of \TeX trieOpSize = 500 // space for ``opcodes'' in the hyphenation patterns dviBufSize = 800 // size of the output buffer; must be a multiple of 8 fileNameSize = 40 // file names shouldn't be longer than this poolName = "TeXformats:TEX.POOL " // string of length |file_name_size|; tells where the string pool appears // \xref[TeXformats] ) type ( // Types in the outer block asciiCode = /* 0..255 */ byte // eight-bit numbers eightBits = /* 0..255 */ byte // unsigned one-byte quantity alphaFile = knuth.File // files that contain textual data byteFile = knuth.File // files that contain binary data poolPointer = /* 0..poolSize */ uint16 // for variables that point into |str_pool| strNumber = /* 0..maxStrings */ uint16 // for variables that point into |str_start| packedAsciiCode = /* 0..255 */ byte // elements of |str_pool| array scaled = int32 // this type is used for scaled integers nonnegativeInteger = /* 0..017777777777 */ uint32 // $0\L x<2^[31]$ smallNumber = /* 0..63 */ byte // this type is self-explanatory glueRatio = float64 // one-word representation of a glue expansion factor quarterword = /* minQuarterword..maxQuarterword */ byte // 1/4 of a word halfword = /* 0..65535 */ uint16 // 1/2 of a word twoChoices = /* 1..2 */ byte // used when there are two variants in a record fourChoices = /* 1..4 */ byte // used when there are four variants in a record twoHalves struct{ data uint32 } fourQuarters = struct { b0 quarterword b1 quarterword b2 quarterword b3 quarterword } memoryWord struct{ data uint32 } wordFile = knuth.File glueOrd = /* normal..filll */ byte // infinity to the 0, 1, 2, or 3 power listStateRecord = struct { modeField int16 headField, tailField halfword pgField, mlField int32 auxField memoryWord } groupCode = /* 0..maxGroupCode */ byte // |save_level| for a level boundary inStateRecord = struct { stateField, indexField quarterword startField, locField, limitField, nameField halfword } internalFontNumber = /* fontBase..fontMax */ byte // |font| in a |char_node| fontIndex = /* 0..fontMemSize */ uint16 // index into |font_info| dviIndex = /* 0..dviBufSize */ uint16 // an index into the output buffer triePointer = /* 0..trieSize */ uint16 // an index into |trie| hyphPointer = /* 0..hyphSize */ uint16 // an index into the ordered hash table ) func (r *memoryWord) gr() *float32 { return (*float32)(unsafe.Add(unsafe.Pointer(&r.data), 0)) } func (r *memoryWord) hh() *twoHalves { return (*twoHalves)(unsafe.Add(unsafe.Pointer(&r.data), 0)) } func (r *memoryWord) int() *int32 { return (*int32)(unsafe.Add(unsafe.Pointer(&r.data), 0)) } func (r *memoryWord) qqqq() *fourQuarters { return (*fourQuarters)(unsafe.Add(unsafe.Pointer(&r.data), 0)) } func (r *twoHalves) b0() *quarterword { return (*quarterword)(unsafe.Add(unsafe.Pointer(&r.data), 2)) } func (r *twoHalves) b1() *quarterword { return (*quarterword)(unsafe.Add(unsafe.Pointer(&r.data), 3)) } func (r *twoHalves) lh() *halfword { return (*halfword)(unsafe.Add(unsafe.Pointer(&r.data), 2)) } func (r *twoHalves) rh() *halfword { return (*halfword)(unsafe.Add(unsafe.Pointer(&r.data), 0)) } type prg struct { stdin, stdout, stderr knuth.File // Global variables bad int32 // is some ``constant'' wrong? xord [256]asciiCode // specifies conversion of input characters xchr [256]char // specifies conversion of output characters nameOfFile [40]char // on some systems this may be a \&[record] variable nameLength/* 0..fileNameSize */ byte // this many characters are actually // relevant in |name_of_file| (the rest are blank) buffer [501]asciiCode // lines of characters being read first/* 0..bufSize */ uint16 // the first unused position in |buffer| last/* 0..bufSize */ uint16 // end of the line just input to |buffer| maxBufStack/* 0..bufSize */ uint16 // largest index used in |buffer| termIn alphaFile // the terminal as an input file termOut alphaFile // the terminal as an output file strPool [32001]packedAsciiCode // the characters strStart [3001]poolPointer // the starting pointers poolPtr poolPointer // first unused position in |str_pool| strPtr strNumber // number of the current string being created initPoolPtr poolPointer // the starting value of |pool_ptr| initStrPtr strNumber // the starting value of |str_ptr| poolFile alphaFile // the string-pool file output by \.[TANGLE] logFile alphaFile // transcript of \TeX\ session selector/* 0..maxSelector */ byte // where to print a message dig [23] /* 0..15 */ byte // digits in a number being output tally int32 // the number of characters recently printed termOffset/* 0..maxPrintLine */ byte // the number of characters on the current terminal line fileOffset/* 0..maxPrintLine */ byte // the number of characters on the current file line trickBuf [73]asciiCode // circular buffer for // pseudoprinting trickCount int32 // threshold for pseudoprinting, explained later firstCount int32 // another variable for pseudoprinting interaction/* batchMode..errorStopMode */ byte // current level of interaction deletionsAllowed bool // is it safe for |error| to call |get_token|? setBoxAllowed bool // is it safe to do a \.[\\setbox] assignment? history/* spotless..fatalErrorStop */ byte // has the source input been clean so far? errorCount/* -1..100 */ int8 // the number of scrolled errors since the // last paragraph ended helpLine [6]strNumber // helps for the next |error| helpPtr/* 0..6 */ byte // the number of help lines present useErrHelp bool // should the |err_help| list be shown? interrupt int32 // should \TeX\ pause for instructions? okToInterrupt bool // should interrupts be observed? arithError bool // has arithmetic overflow occurred recently? remainder scaled // amount subtracted to get an exact division tempPtr halfword // a pointer variable for occasional emergency use mem [30001]memoryWord // the big dynamic storage area loMemMax halfword // the largest location of variable-size memory in use hiMemMin halfword // the smallest location of one-word memory in use varUsed, dynUsed int32 // how much memory is in use avail halfword // head of the list of available one-word nodes memEnd halfword // the last one-word node used in |mem| rover halfword // points to some node in the list of empties // free: packed array [mem_min..mem_max] of boolean; [free cells] // [ \hskip10pt ] was_free: packed array [mem_min..mem_max] of boolean; // [previously free cells] // [ \hskip10pt ] was_mem_end, was_lo_max, was_hi_min: halfword ; // [previous |mem_end|, |lo_mem_max|, and |hi_mem_min|] // [ \hskip10pt ] panicking:boolean; [do we want to check memory constantly?] // [ ] fontInShortDisplay int32 // an internal font number depthThreshold int32 // maximum nesting depth in box displays breadthMax int32 // maximum number of items shown at the same list level nest [41]listStateRecord nestPtr/* 0..nestSize */ byte // first unused location of |nest| maxNestStack/* 0..nestSize */ byte // maximum of |nest_ptr| when pushing curList listStateRecord // the ``top'' semantic state shownMode/* -mmode..mmode */ int16 // most recent mode shown by \.[\\tracingcommands] oldSetting/* 0..maxSelector */ byte sysTime, sysDay, sysMonth, sysYear int32 // date and time supplied by external system eqtb [6106]memoryWord xeqLevel [844]quarterword hash [2367]twoHalves // the hash table hashUsed halfword // allocation pointer for |hash| noNewControlSequence bool // are new identifiers legal? csCount int32 // total number of known identifiers saveStack [601]memoryWord savePtr/* 0..saveSize */ uint16 // first unused entry on |save_stack| maxSaveStack/* 0..saveSize */ uint16 // maximum usage of save stack curLevel quarterword // current nesting level for groups curGroup groupCode // current group type curBoundary/* 0..saveSize */ uint16 // where the current level begins magSet int32 // if nonzero, this magnification should be used henceforth curCmd eightBits // current command set by |get_next| curChr halfword // operand of current command curCs halfword // control sequence found here, zero if none found curTok halfword // packed representative of |cur_cmd| and |cur_chr| inputStack [201]inStateRecord inputPtr/* 0..stackSize */ byte // first unused location of |input_stack| maxInStack/* 0..stackSize */ byte // largest value of |input_ptr| when pushing curInput inStateRecord // the ``top'' input state, according to convention (1) inOpen/* 0..maxInOpen */ byte // the number of lines in the buffer, less one openParens/* 0..maxInOpen */ byte // the number of open text files inputFile [6]alphaFile line int32 // current line number in the current source file lineStack [6]int32 scannerStatus/* normal..absorbing */ byte // can a subfile end now? warningIndex halfword // identifier relevant to non-|normal| scanner status defRef halfword // reference count of token list being defined paramStack [61]halfword // token list pointers for parameters paramPtr/* 0..paramSize */ byte // first unused entry in |param_stack| maxParamStack int32 // largest value of |param_ptr|, will be |<=param_size+9| alignState int32 // group level with respect to current alignment basePtr/* 0..stackSize */ byte // shallowest level shown by |show_context| parLoc halfword // location of `\.[\\par]' in |eqtb| parToken halfword // token representing `\.[\\par]' forceEof bool // should the next \.[\\input] be aborted early? curMark [5]halfword // token lists for marks longState/* call..longOuterCall */ byte // governs the acceptance of \.[\\par] pstack [9]halfword // arguments supplied to a macro curVal int32 // value returned by numeric scanners curValLevel/* intVal..tokVal */ byte // the ``level'' of this value radix smallNumber // |scan_int| sets this to 8, 10, 16, or zero curOrder glueOrd // order of infinity found by |scan_dimen| readFile [16]alphaFile // used for \.[\\read] readOpen [17] /* normal..closed */ byte // state of |read_file[n]| condPtr halfword // top of the condition stack ifLimit/* normal..orCode */ byte // upper bound on |fi_or_else| codes curIf smallNumber // type of conditional being worked on ifLine int32 // line where that conditional began skipLine int32 // skipping began here curName strNumber // name of file just scanned curArea strNumber // file area just scanned, or \.[""] curExt strNumber // file extension just scanned, or \.[""] areaDelimiter poolPointer // the most recent `\.>' or `\.:', if any extDelimiter poolPointer // the relevant `\..', if any texFormatDefault [20]char nameInProgress bool // is a file name being scanned? jobName strNumber // principal file name logOpened bool // has the transcript file been opened? dviFile byteFile // the device-independent output goes here outputFileName strNumber // full name of the output file logName strNumber // full name of the log file tfmFile byteFile buf eightBits fontInfo [20001]memoryWord // the big collection of font data fmemPtr fontIndex // first unused word of |font_info| fontPtr internalFontNumber // largest internal font number in use fontCheck [76]fourQuarters // check sum fontSize [76]scaled // ``at'' size fontDsize [76]scaled // ``design'' size fontParams [76]fontIndex // how many font // parameters are present fontName [76]strNumber // name of the font fontArea [76]strNumber // area of the font fontBc [76]eightBits // beginning (smallest) character code fontEc [76]eightBits // ending (largest) character code fontGlue [76]halfword // glue specification for interword space, |null| if not allocated fontUsed [76]bool // has a character from this font actually appeared in the output? hyphenChar [76]int32 // current \.[\\hyphenchar] values skewChar [76]int32 // current \.[\\skewchar] values bcharLabel [76]fontIndex // start of |lig_kern| program for left boundary character, // |non_address| if there is none fontBchar [76] /* minQuarterword..256+minQuarterword */ uint16 // boundary character, |non_char| if there is none fontFalseBchar [76] /* minQuarterword..256+minQuarterword */ uint16 // |font_bchar| if it doesn't exist in the font, otherwise |non_char| charBase [76]int32 // base addresses for |char_info| widthBase [76]int32 // base addresses for widths heightBase [76]int32 // base addresses for heights depthBase [76]int32 // base addresses for depths italicBase [76]int32 // base addresses for italic corrections ligKernBase [76]int32 // base addresses for ligature/kerning programs kernBase [76]int32 // base addresses for kerns extenBase [76]int32 // base addresses for extensible recipes paramBase [76]int32 // base addresses for font parameters nullCharacter fourQuarters // nonexistent character information totalPages int32 // the number of pages that have been shipped out maxV scaled // maximum height-plus-depth of pages shipped so far maxH scaled // maximum width of pages shipped so far maxPush int32 // deepest nesting of |push| commands encountered so far lastBop int32 // location of previous |bop| in the \.[DVI] output deadCycles int32 // recent outputs that didn't ship anything out doingLeaders bool // are we inside a leader box? c, f quarterword // character and font in current |char_node| ruleHt, ruleDp, ruleWd scaled // size of current rule being output g halfword // current glue specification lq, lr int32 // quantities used in calculations for leaders dviBuf [801]eightBits // buffer for \.[DVI] output halfBuf dviIndex // half of |dvi_buf_size| dviLimit dviIndex // end of the current half buffer dviPtr dviIndex // the next available buffer address dviOffset int32 // |dvi_buf_size| times the number of times the // output buffer has been fully emptied dviGone int32 // the number of bytes already output to |dvi_file| downPtr, rightPtr halfword // heads of the down and right stacks dviH, dviV scaled // a \.[DVI] reader program thinks we are here curH, curV scaled // \TeX\ thinks we are here dviF internalFontNumber // the current font curS int32 // current depth of output box nesting, initially $-1$ totalStretch, totalShrink [4]scaled // glue found by |hpack| or |vpack| lastBadness int32 // badness of the most recently packaged box adjustTail halfword // tail of adjustment list packBeginLine int32 // source file line where the current paragraph // or alignment began; a negative value denotes alignment emptyField twoHalves nullDelimiter fourQuarters curMlist halfword // beginning of mlist to be translated curStyle smallNumber // style code at current place in the list curSize smallNumber // size code corresponding to |cur_style| curMu scaled // the math unit width corresponding to |cur_size| mlistPenalties bool // should |mlist_to_hlist| insert penalties? curF internalFontNumber // the |font| field of a |math_char| curC quarterword // the |character| field of a |math_char| curI fourQuarters // the |char_info| of a |math_char|, // or a lig/kern instruction magicOffset int32 // used to find inter-element spacing curAlign halfword // current position in preamble list curSpan halfword // start of currently spanned columns in preamble list curLoop halfword // place to copy when extending a periodic preamble alignPtr halfword // most recently pushed-down alignment stack node curHead, curTail halfword // adjustment list pointers justBox halfword // the |hlist_node| for the last line of the new paragraph passive halfword // most recent node on passive list printedNode halfword // most recent node that has been printed passNumber halfword // the number of passive nodes allocated on this pass activeWidth [6]scaled // distance from first active node to~|cur_p| curActiveWidth [6]scaled // distance from current active node background [6]scaled // length of an ``empty'' line breakWidth [6]scaled // length being computed after current break noShrinkErrorYet bool // have we complained about infinite shrinkage? curP halfword // the current breakpoint under consideration secondPass bool // is this our second attempt to break this paragraph? finalPass bool // is this our final attempt to break this paragraph? threshold int32 // maximum badness on feasible lines minimalDemerits [4]int32 // best total // demerits known for current line class and position, given the fitness minimumDemerits int32 // best total demerits known for current line class // and position bestPlace [4]halfword // how to achieve // |minimal_demerits| bestPlLine [4]halfword // corresponding // line number discWidth scaled // the length of discretionary material preceding a break easyLine halfword // line numbers |>easy_line| are equivalent in break nodes lastSpecialLine halfword // line numbers |>last_special_line| all have // the same width firstWidth scaled // the width of all lines |<=last_special_line|, if // no \.[\\parshape] has been specified secondWidth scaled // the width of all lines |>last_special_line| firstIndent scaled // left margin to go with |first_width| secondIndent scaled // left margin to go with |second_width| bestBet halfword // use this passive node and its predecessors fewestDemerits int32 // the demerits associated with |best_bet| bestLine halfword // line number following the last line of the new paragraph actualLooseness int32 // the difference between |line_number(best_bet)| // and the optimum |best_line| lineDiff int32 // the difference between the current line number and // the optimum |best_line| hc [66] /* 0..256 */ uint16 // word to be hyphenated hn/* 0..64 */ byte // the number of positions occupied in |hc|; // not always a |small_number| ha, hb halfword // nodes |ha..hb| should be replaced by the hyphenated result hf internalFontNumber // font number of the letters in |hc| hu [64] /* 0..256 */ uint16 // like |hc|, before conversion to lowercase hyfChar int32 // hyphen character of the relevant font curLang, initCurLang asciiCode // current hyphenation table of interest lHyf, rHyf, initLHyf, initRHyf int32 // limits on fragment sizes hyfBchar halfword // boundary character after $c_n$ hyf [65] /* 0..9 */ byte // odd values indicate discretionary hyphens initList halfword // list of punctuation characters preceding the word initLig bool // does |init_list| represent a ligature? initLft bool // if so, did the ligature involve a left boundary? hyphenPassed smallNumber // first hyphen in a ligature, if any curL, curR halfword // characters before and after the cursor curQ halfword // where a ligature should be detached ligStack halfword // unfinished business to the right of the cursor ligaturePresent bool // should a ligature node be made for |cur_l|? lftHit, rtHit bool // did we hit a ligature with a boundary character? trie [8001]twoHalves // |trie_link|, |trie_char|, |trie_op| hyfDistance [500]smallNumber // position |k-j| of $n_j$ hyfNum [500]smallNumber // value of $n_j$ hyfNext [500]quarterword // continuation code opStart [256] /* 0..trieOpSize */ uint16 // offset for current language hyphWord [308]strNumber // exception words hyphList [308]halfword // lists of hyphen positions hyphCount hyphPointer // the number of words in the exception dictionary trieOpHash [1001] /* 0..trieOpSize */ uint16 // trie op codes for quadruples trieUsed [256]quarterword // largest opcode used so far for this language trieOpLang [500]asciiCode // language part of a hashed quadruple trieOpVal [500]quarterword // opcode corresponding to a hashed quadruple trieOpPtr/* 0..trieOpSize */ uint16 // number of stored ops so far trieC [8001]packedAsciiCode // characters to match // \hskip10pt trieO [8001]quarterword // operations to perform // \hskip10pt trieL [8001]triePointer // left subtrie links // \hskip10pt trieR [8001]triePointer // right subtrie links // \hskip10pt triePtr triePointer // the number of nodes in the trie // \hskip10pt trieHash [8001]triePointer // used to identify equivalent subtries trieTaken [8000]bool // does a family start here? // \hskip10pt trieMin [256]triePointer // the first possible slot for each character // \hskip10pt trieMax triePointer // largest location used in |trie| // \hskip10pt trieNotReady bool // is the trie still in linked form? bestHeightPlusDepth scaled // height of the best box, without stretching or // shrinking pageTail halfword // the final node on the current page pageContents/* empty..boxThere */ byte // what is on the current page so far? pageMaxDepth scaled // maximum box depth on page being built bestPageBreak halfword // break here to get the best page known so far leastPageCost int32 // the score for this currently best page bestSize scaled // its |page_goal| pageSoFar [8]scaled // height and glue of the current page lastGlue halfword // used to implement \.[\\lastskip] lastPenalty int32 // used to implement \.[\\lastpenalty] lastKern scaled // used to implement \.[\\lastkern] insertPenalties int32 // sum of the penalties for insertions // that were held over outputActive bool // are we in the midst of an output routine? mainF internalFontNumber // the current font mainI fourQuarters // character information bytes for |cur_l| mainJ fourQuarters // ligature/kern command mainK fontIndex // index into |font_info| mainP halfword // temporary register for list manipulation mainS int32 // space factor value bchar halfword // boundary character of current font, or |non_char| falseBchar halfword // nonexistent character matching |bchar|, or |non_char| cancelBoundary bool // should the left boundary be ignored? insDisc bool // should we insert a discretionary node? curBox halfword // box to be placed into its context afterToken halfword // zero, or a saved token longHelpSeen bool // has the long \.[\\errmessage] help been used? formatIdent strNumber fmtFile wordFile // for input or output of format information readyAlready int32 // a sacrifice of purity for economy writeFile [16]alphaFile writeOpen [18]bool writeLoc halfword // |eqtb| address of \.[\\write] } func (prg *prg) initialize() { // this procedure gets things started properly var ( // Local variables for initialization i int32 k int32 // index into |mem|, |eqtb|, etc. z hyphPointer // runs through the exception dictionary ) prg.xchr[040] = ' ' prg.xchr[041] = '!' prg.xchr[042] = '"' prg.xchr[043] = '#' prg.xchr[044] = '$' prg.xchr[045] = '%' prg.xchr[046] = '&' prg.xchr[047] = '\'' prg.xchr[050] = '(' prg.xchr[051] = ')' prg.xchr[052] = '*' prg.xchr[053] = '+' prg.xchr[054] = ',' prg.xchr[055] = '-' prg.xchr[056] = '.' prg.xchr[057] = '/' prg.xchr[060] = '0' prg.xchr[061] = '1' prg.xchr[062] = '2' prg.xchr[063] = '3' prg.xchr[064] = '4' prg.xchr[065] = '5' prg.xchr[066] = '6' prg.xchr[067] = '7' prg.xchr[070] = '8' prg.xchr[071] = '9' prg.xchr[072] = ':' prg.xchr[073] = ';' prg.xchr[074] = '<' prg.xchr[075] = '=' prg.xchr[076] = '>' prg.xchr[077] = '?' prg.xchr[0100] = '@' prg.xchr[0101] = 'A' prg.xchr[0102] = 'B' prg.xchr[0103] = 'C' prg.xchr[0104] = 'D' prg.xchr[0105] = 'E' prg.xchr[0106] = 'F' prg.xchr[0107] = 'G' prg.xchr[0110] = 'H' prg.xchr[0111] = 'I' prg.xchr[0112] = 'J' prg.xchr[0113] = 'K' prg.xchr[0114] = 'L' prg.xchr[0115] = 'M' prg.xchr[0116] = 'N' prg.xchr[0117] = 'O' prg.xchr[0120] = 'P' prg.xchr[0121] = 'Q' prg.xchr[0122] = 'R' prg.xchr[0123] = 'S' prg.xchr[0124] = 'T' prg.xchr[0125] = 'U' prg.xchr[0126] = 'V' prg.xchr[0127] = 'W' prg.xchr[0130] = 'X' prg.xchr[0131] = 'Y' prg.xchr[0132] = 'Z' prg.xchr[0133] = '[' prg.xchr[0134] = '\\' prg.xchr[0135] = ']' prg.xchr[0136] = '^' prg.xchr[0137] = '_' prg.xchr[0140] = '`' prg.xchr[0141] = 'a' prg.xchr[0142] = 'b' prg.xchr[0143] = 'c' prg.xchr[0144] = 'd' prg.xchr[0145] = 'e' prg.xchr[0146] = 'f' prg.xchr[0147] = 'g' prg.xchr[0150] = 'h' prg.xchr[0151] = 'i' prg.xchr[0152] = 'j' prg.xchr[0153] = 'k' prg.xchr[0154] = 'l' prg.xchr[0155] = 'm' prg.xchr[0156] = 'n' prg.xchr[0157] = 'o' prg.xchr[0160] = 'p' prg.xchr[0161] = 'q' prg.xchr[0162] = 'r' prg.xchr[0163] = 's' prg.xchr[0164] = 't' prg.xchr[0165] = 'u' prg.xchr[0166] = 'v' prg.xchr[0167] = 'w' prg.xchr[0170] = 'x' prg.xchr[0171] = 'y' prg.xchr[0172] = 'z' prg.xchr[0173] = '{' prg.xchr[0174] = '|' prg.xchr[0175] = '}' prg.xchr[0176] = '~' for ii := int32(0); ii <= 037; ii++ { i = ii _ = i prg.xchr[i] = ' ' } for ii := int32(0177); ii <= 0377; ii++ { i = ii _ = i prg.xchr[i] = ' ' } for ii := int32(firstTextChar); ii <= lastTextChar; ii++ { i = ii _ = i prg.xord[char(i)] = byte(invalidCode) } for ii := int32(0200); ii <= 0377; ii++ { i = ii _ = i prg.xord[prg.xchr[i]] = byte(i) } for ii := int32(0); ii <= 0176; ii++ { i = ii _ = i prg.xord[prg.xchr[i]] = byte(i) } prg.interaction = byte(errorStopMode) prg.deletionsAllowed = true prg.setBoxAllowed = true prg.errorCount = 0 // |history| is initialized elsewhere prg.helpPtr = 0 prg.useErrHelp = false prg.interrupt = 0 prg.okToInterrupt = true // was_mem_end:=mem_min; [indicate that everything was previously free] // was_lo_max:=mem_min; was_hi_min:=mem_max; // panicking:=false; // [ ] prg.nestPtr = 0 prg.maxNestStack = 0 prg.curList.modeField = int16(vmode) prg.curList.headField = uint16(30000 - 1) prg.curList.tailField = uint16(30000 - 1) *prg.curList.auxField.int() = -65536000 prg.curList.mlField = 0 prg.curList.pgField = 0 prg.shownMode = 0 // Start a new current page prg.pageContents = byte(empty) prg.pageTail = uint16(30000 - 2) *(*prg.mem[30000-2].hh()).rh() = 0 prg.lastGlue = 65535 prg.lastPenalty = 0 prg.lastKern = 0 prg.pageSoFar[7] = 0 prg.pageMaxDepth = 0 for ii := int32(intBase); ii <= eqtbSize; ii++ { k = ii _ = k prg.xeqLevel[k-5263] = byte(levelOne) } prg.noNewControlSequence = true // new identifiers are usually forbidden *prg.hash[hashBase-514].lh() = 0 *prg.hash[hashBase-514].rh() = 0 for ii := int32(hashBase + 1); ii <= undefinedControlSequence-1; ii++ { k = ii _ = k prg.hash[k-514] = prg.hash[hashBase-514] } prg.savePtr = 0 prg.curLevel = byte(levelOne) prg.curGroup = byte(bottomLevel) prg.curBoundary = 0 prg.maxSaveStack = 0 prg.magSet = 0 prg.curMark[topMarkCode] = 0 prg.curMark[firstMarkCode] = 0 prg.curMark[botMarkCode] = 0 prg.curMark[splitFirstMarkCode] = 0 prg.curMark[splitBotMarkCode] = 0 prg.curVal = 0 prg.curValLevel = byte(intVal) prg.radix = 0 prg.curOrder = byte(normal) for ii := int32(0); ii <= 16; ii++ { k = ii _ = k prg.readOpen[k] = byte(closed) } prg.condPtr = 0 prg.ifLimit = byte(normal) prg.curIf = 0 prg.ifLine = 0 strcopy(prg.texFormatDefault[:], "TeXformats:plain.fmt") // \xref[TeXformats] // \xref[plain] // \xref[system dependencies] for ii := int32(fontBase); ii <= fontMax; ii++ { k = ii _ = k prg.fontUsed[k] = false } prg.nullCharacter.b0 = byte(minQuarterword) prg.nullCharacter.b1 = byte(minQuarterword) prg.nullCharacter.b2 = byte(minQuarterword) prg.nullCharacter.b3 = byte(minQuarterword) prg.totalPages = 0 prg.maxV = 0 prg.maxH = 0 prg.maxPush = 0 prg.lastBop = -1 prg.doingLeaders = false prg.deadCycles = 0 prg.curS = -1 prg.halfBuf = uint16(dviBufSize / 2) prg.dviLimit = uint16(dviBufSize) prg.dviPtr = 0 prg.dviOffset = 0 prg.dviGone = 0 prg.downPtr = 0 prg.rightPtr = 0 prg.adjustTail = 0 prg.lastBadness = 0 prg.packBeginLine = 0 *prg.emptyField.rh() = uint16(empty) *prg.emptyField.lh() = 0 prg.nullDelimiter.b0 = 0 prg.nullDelimiter.b1 = byte(minQuarterword) prg.nullDelimiter.b2 = 0 prg.nullDelimiter.b3 = byte(minQuarterword) prg.alignPtr = 0 prg.curAlign = 0 prg.curSpan = 0 prg.curLoop = 0 prg.curHead = 0 prg.curTail = 0 for ii := int32(0); ii <= hyphSize; ii++ { z = hyphPointer(ii) _ = z prg.hyphWord[z] = 0 prg.hyphList[z] = 0 } prg.hyphCount = 0 prg.outputActive = false prg.insertPenalties = 0 prg.ligaturePresent = false prg.cancelBoundary = false prg.lftHit = false prg.rtHit = false prg.insDisc = false prg.afterToken = 0 prg.longHelpSeen = false prg.formatIdent = 0 for ii := int32(0); ii <= 17; ii++ { k = ii _ = k prg.writeOpen[k] = false } // Initialize table entries (done by \.[INITEX] only) for ii := int32(memBot + 1); ii <= memBot+glueSpecSize+glueSpecSize+glueSpecSize+glueSpecSize+glueSpecSize-1; ii++ { k = ii _ = k *prg.mem[k].int() = 0 } // all glue dimensions are zeroed // \xref[data structure assumptions] k = memBot for k <= memBot+glueSpecSize+glueSpecSize+glueSpecSize+glueSpecSize+glueSpecSize-1 { // set first words of glue specifications *(*prg.mem[k].hh()).rh() = uint16(0 + 1) *(*prg.mem[k].hh()).b0() = byte(normal) *(*prg.mem[k].hh()).b1() = byte(normal) k = k + glueSpecSize } *prg.mem[memBot+glueSpecSize+2].int() = 0200000 *(*prg.mem[memBot+glueSpecSize].hh()).b0() = byte(fil) *prg.mem[memBot+glueSpecSize+glueSpecSize+2].int() = 0200000 *(*prg.mem[memBot+glueSpecSize+glueSpecSize].hh()).b0() = byte(fill) *prg.mem[memBot+glueSpecSize+glueSpecSize+glueSpecSize+2].int() = 0200000 *(*prg.mem[memBot+glueSpecSize+glueSpecSize+glueSpecSize].hh()).b0() = byte(fil) *prg.mem[memBot+glueSpecSize+glueSpecSize+glueSpecSize+3].int() = 0200000 *(*prg.mem[memBot+glueSpecSize+glueSpecSize+glueSpecSize].hh()).b1() = byte(fil) *prg.mem[memBot+glueSpecSize+glueSpecSize+glueSpecSize+glueSpecSize+2].int() = -0200000 *(*prg.mem[memBot+glueSpecSize+glueSpecSize+glueSpecSize+glueSpecSize].hh()).b0() = byte(fil) prg.rover = uint16(memBot + glueSpecSize + glueSpecSize + glueSpecSize + glueSpecSize + glueSpecSize - 1 + 1) *(*prg.mem[prg.rover].hh()).rh() = 65535 // now initialize the dynamic memory *(*prg.mem[prg.rover].hh()).lh() = 1000 // which is a 1000-word available node *(*prg.mem[int32(prg.rover)+1].hh()).lh() = prg.rover *(*prg.mem[int32(prg.rover)+1].hh()).rh() = prg.rover prg.loMemMax = uint16(int32(prg.rover) + 1000) *(*prg.mem[prg.loMemMax].hh()).rh() = 0 *(*prg.mem[prg.loMemMax].hh()).lh() = 0 for ii := int32(30000 - 13); ii <= 30000; ii++ { k = ii _ = k prg.mem[k] = prg.mem[prg.loMemMax] } // clear list heads // Initialize the special list heads and constant nodes *(*prg.mem[30000-10].hh()).lh() = uint16(07777 + frozenEndTemplate) // |link(omit_template)=null| *(*prg.mem[30000-9].hh()).rh() = uint16(maxQuarterword + 1) *(*prg.mem[30000-9].hh()).lh() = 0 *(*prg.mem[30000-7].hh()).b0() = byte(hyphenated) *(*prg.mem[30000-7+1].hh()).lh() = 65535 *(*prg.mem[30000-7].hh()).b1() = 0 // the |subtype| is never examined by the algorithm *(*prg.mem[30000].hh()).b1() = byte(255 + minQuarterword) *(*prg.mem[30000].hh()).b0() = byte(splitUp) *(*prg.mem[30000].hh()).rh() = 30000 *(*prg.mem[30000-2].hh()).b0() = byte(glueNode) *(*prg.mem[30000-2].hh()).b1() = byte(normal) prg.avail = 0 prg.memEnd = 30000 prg.hiMemMin = uint16(30000 - 13) // initialize the one-word memory prg.varUsed = memBot + glueSpecSize + glueSpecSize + glueSpecSize + glueSpecSize + glueSpecSize - 1 + 1 - memBot prg.dynUsed = hiMemStatUsage // initialize statistics *(*prg.eqtb[undefinedControlSequence-1].hh()).b0() = byte(undefinedCs) *(*prg.eqtb[undefinedControlSequence-1].hh()).rh() = 0 *(*prg.eqtb[undefinedControlSequence-1].hh()).b1() = byte(levelZero) for ii := int32(activeBase); ii <= undefinedControlSequence-1; ii++ { k = ii _ = k prg.eqtb[k-1] = prg.eqtb[undefinedControlSequence-1] } *(*prg.eqtb[glueBase-1].hh()).rh() = uint16(memBot) *(*prg.eqtb[glueBase-1].hh()).b1() = byte(levelOne) *(*prg.eqtb[glueBase-1].hh()).b0() = byte(glueRef) for ii := int32(glueBase + 1); ii <= localBase-1; ii++ { k = ii _ = k prg.eqtb[k-1] = prg.eqtb[glueBase-1] } *(*prg.mem[memBot].hh()).rh() = uint16(int32(*(*prg.mem[memBot].hh()).rh()) + localBase - glueBase) *(*prg.eqtb[parShapeLoc-1].hh()).rh() = 0 *(*prg.eqtb[parShapeLoc-1].hh()).b0() = byte(shapeRef) *(*prg.eqtb[parShapeLoc-1].hh()).b1() = byte(levelOne) for ii := int32(outputRoutineLoc); ii <= toksBase+255; ii++ { k = ii _ = k prg.eqtb[k-1] = prg.eqtb[undefinedControlSequence-1] } *(*prg.eqtb[boxBase+0-1].hh()).rh() = 0 *(*prg.eqtb[boxBase-1].hh()).b0() = byte(boxRef) *(*prg.eqtb[boxBase-1].hh()).b1() = byte(levelOne) for ii := int32(boxBase + 1); ii <= boxBase+255; ii++ { k = ii _ = k prg.eqtb[k-1] = prg.eqtb[boxBase-1] } *(*prg.eqtb[curFontLoc-1].hh()).rh() = uint16(fontBase) *(*prg.eqtb[curFontLoc-1].hh()).b0() = byte(data) *(*prg.eqtb[curFontLoc-1].hh()).b1() = byte(levelOne) for ii := int32(mathFontBase); ii <= mathFontBase+47; ii++ { k = ii _ = k prg.eqtb[k-1] = prg.eqtb[curFontLoc-1] } *(*prg.eqtb[catCodeBase-1].hh()).rh() = 0 *(*prg.eqtb[catCodeBase-1].hh()).b0() = byte(data) *(*prg.eqtb[catCodeBase-1].hh()).b1() = byte(levelOne) for ii := int32(catCodeBase + 1); ii <= intBase-1; ii++ { k = ii _ = k prg.eqtb[k-1] = prg.eqtb[catCodeBase-1] } for ii := int32(0); ii <= 255; ii++ { k = ii _ = k *(*prg.eqtb[catCodeBase+k-1].hh()).rh() = uint16(otherChar) *(*prg.eqtb[mathCodeBase+k-1].hh()).rh() = uint16(k + 0) *(*prg.eqtb[sfCodeBase+k-1].hh()).rh() = 1000 } *(*prg.eqtb[catCodeBase+carriageReturn-1].hh()).rh() = uint16(carRet) *(*prg.eqtb[catCodeBase+' '-1].hh()).rh() = uint16(spacer) *(*prg.eqtb[catCodeBase+'\\'-1].hh()).rh() = uint16(escape) *(*prg.eqtb[catCodeBase+'%'-1].hh()).rh() = uint16(comment) *(*prg.eqtb[catCodeBase+invalidCode-1].hh()).rh() = uint16(invalidChar) *(*prg.eqtb[catCodeBase+nullCode-1].hh()).rh() = uint16(ignore) for ii := int32('0'); ii <= '9'; ii++ { k = ii _ = k *(*prg.eqtb[mathCodeBase+k-1].hh()).rh() = uint16(k + 070000 + 0) } for ii := int32('A'); ii <= 'Z'; ii++ { k = ii _ = k *(*prg.eqtb[catCodeBase+k-1].hh()).rh() = uint16(letter) *(*prg.eqtb[catCodeBase+k+'a'-'A'-1].hh()).rh() = uint16(letter) *(*prg.eqtb[mathCodeBase+k-1].hh()).rh() = uint16(k + 070000 + 0400 + 0) *(*prg.eqtb[mathCodeBase+k+'a'-'A'-1].hh()).rh() = uint16(k + 'a' - 'A' + 070000 + 0400 + 0) *(*prg.eqtb[lcCodeBase+k-1].hh()).rh() = uint16(k + 'a' - 'A') *(*prg.eqtb[lcCodeBase+k+'a'-'A'-1].hh()).rh() = uint16(k + 'a' - 'A') *(*prg.eqtb[ucCodeBase+k-1].hh()).rh() = uint16(k) *(*prg.eqtb[ucCodeBase+k+'a'-'A'-1].hh()).rh() = uint16(k) *(*prg.eqtb[sfCodeBase+k-1].hh()).rh() = 999 } for ii := int32(intBase); ii <= delCodeBase-1; ii++ { k = ii _ = k *prg.eqtb[k-1].int() = 0 } *prg.eqtb[intBase+magCode-1].int() = 1000 *prg.eqtb[intBase+toleranceCode-1].int() = 10000 *prg.eqtb[intBase+hangAfterCode-1].int() = 1 *prg.eqtb[intBase+maxDeadCyclesCode-1].int() = 25 *prg.eqtb[intBase+escapeCharCode-1].int() = '\\' *prg.eqtb[intBase+endLineCharCode-1].int() = carriageReturn for ii := int32(0); ii <= 255; ii++ { k = ii _ = k *prg.eqtb[delCodeBase+k-1].int() = -1 } *prg.eqtb[delCodeBase+'.'-1].int() = 0 // this null delimiter is used in error recovery for ii := int32(dimenBase); ii <= eqtbSize; ii++ { k = ii _ = k *prg.eqtb[k-1].int() = 0 } prg.hashUsed = uint16(frozenControlSequence) // nothing is used prg.csCount = 0 *(*prg.eqtb[frozenDontExpand-1].hh()).b0() = byte(dontExpand) *prg.hash[frozenDontExpand-514].rh() = 502 // \xref[notexpanded:] prg.fontPtr = byte(fontBase) prg.fmemPtr = 7 prg.fontName[fontBase] = /* "nullfont" */ 801 prg.fontArea[fontBase] = /* "" */ 338 prg.hyphenChar[fontBase] = '-' prg.skewChar[fontBase] = -1 prg.bcharLabel[fontBase] = uint16(nonAddress) prg.fontBchar[fontBase] = uint16(256 + minQuarterword) prg.fontFalseBchar[fontBase] = uint16(256 + minQuarterword) prg.fontBc[fontBase] = 1 prg.fontEc[fontBase] = 0 prg.fontSize[fontBase] = 0 prg.fontDsize[fontBase] = 0 prg.charBase[fontBase] = 0 prg.widthBase[fontBase] = 0 prg.heightBase[fontBase] = 0 prg.depthBase[fontBase] = 0 prg.italicBase[fontBase] = 0 prg.ligKernBase[fontBase] = 0 prg.kernBase[fontBase] = 0 prg.extenBase[fontBase] = 0 prg.fontGlue[fontBase] = 0 prg.fontParams[fontBase] = 7 prg.paramBase[fontBase] = -1 for ii := int32(0); ii <= 6; ii++ { k = ii _ = k *prg.fontInfo[k].int() = 0 } for ii := int32(-trieOpSize); ii <= trieOpSize; ii++ { k = ii _ = k prg.trieOpHash[k+500] = 0 } for ii := int32(0); ii <= 255; ii++ { k = ii _ = k prg.trieUsed[k] = byte(minQuarterword) } prg.trieOpPtr = 0 prg.trieNotReady = true prg.trieL[0] = 0 prg.trieC[0] = 0 prg.triePtr = 0 *prg.hash[frozenProtection-514].rh() = 1190 // \xref[inaccessible] prg.formatIdent = /* " (INITEX)" */ 1257 *prg.hash[endWrite-514].rh() = 1296 *(*prg.eqtb[endWrite-1].hh()).b1() = byte(levelOne) *(*prg.eqtb[endWrite-1].hh()).b0() = byte(outerCall) *(*prg.eqtb[endWrite-1].hh()).rh() = 0 } // \4 // Basic printing procedures func (prg *prg) printLn() { switch prg.selector { case termAndLog: prg.termOut.Writeln() prg.logFile.Writeln() prg.termOffset = 0 prg.fileOffset = 0 case logOnly: prg.logFile.Writeln() prg.fileOffset = 0 case termOnly: prg.termOut.Writeln() prg.termOffset = 0 case noPrint, pseudo, newString: default: prg.writeFile[prg.selector].Writeln() } } // |tally| is not affected func (prg *prg) printChar(s asciiCode) { if int32(s) == *prg.eqtb[intBase+newLineCharCode-1].int() { // Character |s| is the current new-line character if int32(prg.selector) < pseudo { prg.printLn() goto exit } } switch prg.selector { case termAndLog: prg.termOut.Write(string(rune(prg.xchr[s]))) prg.logFile.Write(string(rune(prg.xchr[s]))) prg.termOffset = byte(int32(prg.termOffset) + 1) prg.fileOffset = byte(int32(prg.fileOffset) + 1) if int32(prg.termOffset) == maxPrintLine { prg.termOut.Writeln() prg.termOffset = 0 } if int32(prg.fileOffset) == maxPrintLine { prg.logFile.Writeln() prg.fileOffset = 0 } case logOnly: prg.logFile.Write(string(rune(prg.xchr[s]))) prg.fileOffset = byte(int32(prg.fileOffset) + 1) if int32(prg.fileOffset) == maxPrintLine { prg.printLn() } case termOnly: prg.termOut.Write(string(rune(prg.xchr[s]))) prg.termOffset = byte(int32(prg.termOffset) + 1) if int32(prg.termOffset) == maxPrintLine { prg.printLn() } case noPrint: case pseudo: if prg.tally < prg.trickCount { prg.trickBuf[prg.tally%errorLine] = s } case newString: if int32(prg.poolPtr) < poolSize { prg.strPool[prg.poolPtr] = s prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } // we drop characters if the string space is full default: prg.writeFile[prg.selector].Write(string(rune(prg.xchr[s]))) } prg.tally = prg.tally + 1 exit: } func (prg *prg) print(s int32) { var ( j poolPointer // current character code position nl int32 // new-line character to restore ) if s >= int32(prg.strPtr) { s = /* "???" */ 259 } else if s < 256 { if s < 0 { s = /* "???" */ 259 } else { if int32(prg.selector) > pseudo { prg.printChar(asciiCode(s)) goto exit // internal strings are not expanded } if s == *prg.eqtb[intBase+newLineCharCode-1].int() { if int32(prg.selector) < pseudo { prg.printLn() goto exit } } nl = *prg.eqtb[intBase+newLineCharCode-1].int() *prg.eqtb[intBase+newLineCharCode-1].int() = -1 // temporarily disable new-line character j = prg.strStart[s] for int32(j) < int32(prg.strStart[s+1]) { prg.printChar(prg.strPool[j]) j = uint16(int32(j) + 1) } *prg.eqtb[intBase+newLineCharCode-1].int() = nl goto exit } } j = prg.strStart[s] for int32(j) < int32(prg.strStart[s+1]) { prg.printChar(prg.strPool[j]) j = uint16(int32(j) + 1) } exit: } func (prg *prg) slowPrint(s int32) { // prints string |s| var ( j poolPointer // current character code position ) if s >= int32(prg.strPtr) || s < 256 { prg.print(s) } else { j = prg.strStart[s] for int32(j) < int32(prg.strStart[s+1]) { prg.print(int32(prg.strPool[j])) j = uint16(int32(j) + 1) } } } func (prg *prg) printNl(s strNumber) { if int32(prg.termOffset) > 0 && prg.selector&1 != 0 || int32(prg.fileOffset) > 0 && int32(prg.selector) >= logOnly { prg.printLn() } prg.print(int32(s)) } func (prg *prg) printEsc(s strNumber) { // prints escape character, then |s| var ( c int32 // the escape character code ) c = *prg.eqtb[intBase+escapeCharCode-1].int() if c >= 0 { if c < 256 { prg.print(c) } } prg.slowPrint(int32(s)) } func (prg *prg) printTheDigs(k eightBits) { for int32(k) > 0 { k = byte(int32(k) - 1) if int32(prg.dig[k]) < 10 { prg.printChar(asciiCode('0' + int32(prg.dig[k]))) } else { prg.printChar(asciiCode('A' - 10 + int32(prg.dig[k]))) } } } func (prg *prg) printInt(n int32) { // prints an integer in decimal form var ( k/* 0..23 */ byte // index to current digit; we assume that $\vert n\vert<10^[23]$ m int32 // used to negate |n| in possibly dangerous cases ) k = 0 if n < 0 { prg.printChar(asciiCode('-')) if n > -100000000 { n = -n } else { m = -1 - n n = m / 10 m = m%10 + 1 k = 1 if m < 10 { prg.dig[0] = byte(m) } else { prg.dig[0] = 0 n = n + 1 } } } for { prg.dig[k] = byte(n % 10) n = n / 10 k = byte(int32(k) + 1) if n == 0 { break } } prg.printTheDigs(k) } func (prg *prg) printCs(p int32) { if p < hashBase { if p >= singleBase { if p == nullCs { prg.printEsc(strNumber( /* "csname" */ 504)) prg.printEsc(strNumber( /* "endcsname" */ 505)) prg.printChar(asciiCode(' ')) } else { prg.printEsc(strNumber(p - singleBase)) if int32(*(*prg.eqtb[catCodeBase+p-singleBase-1].hh()).rh()) == letter { prg.printChar(asciiCode(' ')) } } } else if p < activeBase { prg.printEsc(strNumber( /* "IMPOSSIBLE." */ 506)) } else { prg.print(p - activeBase) } } else if p >= undefinedControlSequence { prg.printEsc(strNumber( /* "IMPOSSIBLE." */ 506)) } else if int32(*prg.hash[p-514].rh()) < 0 || int32(*prg.hash[p-514].rh()) >= int32(prg.strPtr) { prg.printEsc(strNumber( /* "NONEXISTENT." */ 507)) } else { prg.printEsc(*prg.hash[p-514].rh()) prg.printChar(asciiCode(' ')) } } func (prg *prg) sprintCs(p halfword) { if int32(p) < hashBase { if int32(p) < singleBase { prg.print(int32(p) - activeBase) } else if int32(p) < nullCs { prg.printEsc(strNumber(int32(p) - singleBase)) } else { prg.printEsc(strNumber( /* "csname" */ 504)) prg.printEsc(strNumber( /* "endcsname" */ 505)) } } else { prg.printEsc(*prg.hash[p-514].rh()) } } func (prg *prg) printFileName(n, a, e int32) { prg.slowPrint(a) prg.slowPrint(n) prg.slowPrint(e) } func (prg *prg) printSize(s int32) { if s == textSize { prg.printEsc(strNumber( /* "textfont" */ 412)) } else if s == scriptSize { prg.printEsc(strNumber( /* "scriptfont" */ 413)) } else { prg.printEsc(strNumber( /* "scriptscriptfont" */ 414)) } } func (prg *prg) printWriteWhatsit(s strNumber, p halfword) { prg.printEsc(s) if int32(*(*prg.mem[int32(p)+1].hh()).lh()) < 16 { prg.printInt(int32(*(*prg.mem[int32(p)+1].hh()).lh())) } else if int32(*(*prg.mem[int32(p)+1].hh()).lh()) == 16 { prg.printChar(asciiCode('*')) } else { prg.printChar(asciiCode('-')) } } // \2 // \4\hskip-\fontdimen2\font // procedure debug_help; // forward; [ ] func (prg *prg) jumpOut() { panic(signal(endOfTex)) } func (prg *prg) error1() { var ( c asciiCode // what the user types s1, s2, s3, s4 int32 // used to save global variables when deleting tokens ) if int32(prg.history) < errorMessageIssued { prg.history = byte(errorMessageIssued) } prg.printChar(asciiCode('.')) prg.showContext() if int32(prg.interaction) == errorStopMode { for true { continue1: if int32(prg.interaction) != errorStopMode { goto exit } prg.clearForErrorPrompt() { prg.print( /* "? " */ 264) prg.termInput() } // \xref[?\relax] if int32(prg.last) == int32(prg.first) { goto exit } c = prg.buffer[prg.first] if int32(c) >= 'a' { c = byte(int32(c) + 'A' - 'a') } // convert to uppercase // Interpret code |c| and |return| if done switch c { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': if prg.deletionsAllowed { s1 = int32(prg.curTok) s2 = int32(prg.curCmd) s3 = int32(prg.curChr) s4 = prg.alignState prg.alignState = 1000000 prg.okToInterrupt = false if int32(prg.last) > int32(prg.first)+1 && int32(prg.buffer[int32(prg.first)+1]) >= '0' && int32(prg.buffer[int32(prg.first)+1]) <= '9' { c = byte(int32(c)*10 + int32(prg.buffer[int32(prg.first)+1]) - '0'*11) } else { c = byte(int32(c) - '0') } for int32(c) > 0 { prg.getToken() // one-level recursive call of |error| is possible c = byte(int32(c) - 1) } prg.curTok = uint16(s1) prg.curCmd = byte(s2) prg.curChr = uint16(s3) prg.alignState = s4 prg.okToInterrupt = true { prg.helpPtr = 2 prg.helpLine[1] = /* "I have just deleted some text, as you asked." */ 279 prg.helpLine[0] = /* "You can now delete more, or insert, or whatever." */ 280 } prg.showContext() goto continue1 } // \4\4 // ["D"=]68: begin debug_help; goto continue; end; [ ] // "E"= case 'E': if int32(prg.basePtr) > 0 { if int32(prg.inputStack[prg.basePtr].nameField) >= 256 { prg.printNl(strNumber( /* "You want to edit file " */ 265)) // \xref[You want to edit file x] prg.slowPrint(int32(prg.inputStack[prg.basePtr].nameField)) prg.print( /* " at line " */ 266) prg.printInt(prg.line) prg.interaction = byte(scrollMode) prg.jumpOut() } } // "H"= case 'H': // Print the help information and |goto continue| if prg.useErrHelp { prg.giveErrHelp() prg.useErrHelp = false } else { if int32(prg.helpPtr) == 0 { prg.helpPtr = 2 prg.helpLine[1] = /* "Sorry, I don't know how to help in this situation." */ 281 prg.helpLine[0] = /* "Maybe you should try asking a human?" */ 282 } for { prg.helpPtr = byte(int32(prg.helpPtr) - 1) prg.print(int32(prg.helpLine[prg.helpPtr])) prg.printLn() if int32(prg.helpPtr) == 0 { break } } } { prg.helpPtr = 4 prg.helpLine[3] = /* "Sorry, I already gave what help I could..." */ 283 prg.helpLine[2] = /* "Maybe you should try asking a human?" */ 282 prg.helpLine[1] = /* "An error might have occurred before I noticed any problems." */ 284 prg.helpLine[0] = /* "``If all else fails, read the instructions.''" */ 285 } goto continue1 // "I"= case 'I': // Introduce new material from the terminal and |return| prg.beginFileReading() // enter a new syntactic level for terminal input // now |state=mid_line|, so an initial blank space will count as a blank if int32(prg.last) > int32(prg.first)+1 { prg.curInput.locField = uint16(int32(prg.first) + 1) prg.buffer[prg.first] = ' ' } else { { prg.print( /* "insert>" */ 278) prg.termInput() } prg.curInput.locField = prg.first // \xref[insert>] } prg.first = prg.last prg.curInput.limitField = uint16(int32(prg.last) - 1) // no |end_line_char| ends this line // no |end_line_char| ends this line goto exit // "Q"= case 'Q', 'R', 'S': // Change the interaction level and |return| prg.errorCount = 0 prg.interaction = byte(batchMode + int32(c) - 'Q') prg.print( /* "OK, entering " */ 273) switch c { case 'Q': prg.printEsc(strNumber( /* "batchmode" */ 274)) prg.selector = byte(int32(prg.selector) - 1) // "R"= case 'R': prg.printEsc(strNumber( /* "nonstopmode" */ 275)) // "S"= case 'S': prg.printEsc(strNumber( /* "scrollmode" */ 276)) } // there are no other cases prg.print( /* "..." */ 277) prg.printLn() goto exit // "X"= case 'X': prg.interaction = byte(scrollMode) prg.jumpOut() default: } // Print the menu of available options { prg.print( /* "Type to proceed, S to scroll future error messages," */ 267) // \xref[Type to proceed...] prg.printNl(strNumber( /* "R to run without stopping, Q to run quietly," */ 268)) prg.printNl(strNumber( /* "I to insert something, " */ 269)) if int32(prg.basePtr) > 0 { if int32(prg.inputStack[prg.basePtr].nameField) >= 256 { prg.print( /* "E to edit your file," */ 270) } } if prg.deletionsAllowed { prg.printNl(strNumber( /* "1 or ... or 9 to ignore the next 1 to 9 tokens of input," */ 271)) } prg.printNl(strNumber( /* "H for help, X to quit." */ 272)) } } } prg.errorCount = int8(int32(prg.errorCount) + 1) if int32(prg.errorCount) == 100 { prg.printNl(strNumber( /* "(That makes 100 errors; please try again.)" */ 263)) // \xref[That makes 100 errors...] prg.history = byte(fatalErrorStop) prg.jumpOut() } // Put help message on the transcript file if int32(prg.interaction) > batchMode { prg.selector = byte(int32(prg.selector) - 1) } // avoid terminal output if prg.useErrHelp { prg.printLn() prg.giveErrHelp() } else { for int32(prg.helpPtr) > 0 { prg.helpPtr = byte(int32(prg.helpPtr) - 1) prg.printNl(prg.helpLine[prg.helpPtr]) } } prg.printLn() if int32(prg.interaction) > batchMode { prg.selector = byte(int32(prg.selector) + 1) } // re-enable terminal output prg.printLn() exit: } func (prg *prg) fatalError(s strNumber) { prg.normalizeSelector() { if int32(prg.interaction) == errorStopMode { } prg.printNl(strNumber( /* "! " */ 262)) prg.print( /* "Emergency stop" */ 287) } { prg.helpPtr = 1 prg.helpLine[0] = s } { if int32(prg.interaction) == errorStopMode { prg.interaction = byte(scrollMode) } if prg.logOpened { prg.error1() } /* if interaction>batch_mode then debug_help; [ ] */ prg.history = byte(fatalErrorStop) prg.jumpOut() } // \xref[Emergency stop] } func (prg *prg) overflow(s strNumber, n int32) { prg.normalizeSelector() { if int32(prg.interaction) == errorStopMode { } prg.printNl(strNumber( /* "! " */ 262)) prg.print( /* "TeX capacity exceeded, sorry [" */ 288) } // \xref[TeX capacity exceeded ...] prg.print(int32(s)) prg.printChar(asciiCode('=')) prg.printInt(n) prg.printChar(asciiCode(']')) { prg.helpPtr = 2 prg.helpLine[1] = /* "If you really absolutely need more capacity," */ 289 prg.helpLine[0] = /* "you can ask a wizard to enlarge me." */ 290 } { if int32(prg.interaction) == errorStopMode { prg.interaction = byte(scrollMode) } if prg.logOpened { prg.error1() } /* if interaction>batch_mode then debug_help; [ ] */ prg.history = byte(fatalErrorStop) prg.jumpOut() } } func (prg *prg) confusion(s strNumber) { prg.normalizeSelector() if int32(prg.history) < errorMessageIssued { { if int32(prg.interaction) == errorStopMode { } prg.printNl(strNumber( /* "! " */ 262)) prg.print( /* "This can't happen (" */ 291) } prg.print(int32(s)) prg.printChar(asciiCode(')')) // \xref[This can't happen] { prg.helpPtr = 1 prg.helpLine[0] = /* "I'm broken. Please show this to someone who can fix can fix" */ 292 } } else { { if int32(prg.interaction) == errorStopMode { } prg.printNl(strNumber( /* "! " */ 262)) prg.print( /* "I can't go on meeting you like this" */ 293) } // \xref[I can't go on...] { prg.helpPtr = 2 prg.helpLine[1] = /* "One of your faux pas seems to have wounded me deeply..." */ 294 prg.helpLine[0] = /* "in fact, I'm barely conscious. Please fix it and try again." */ 295 } } { if int32(prg.interaction) == errorStopMode { prg.interaction = byte(scrollMode) } if prg.logOpened { prg.error1() } /* if interaction>batch_mode then debug_help; [ ] */ prg.history = byte(fatalErrorStop) prg.jumpOut() } } // 5. // tangle:pos tex.web:268:3: // The overall \TeX\ program begins with the heading just shown, after which // comes a bunch of procedure declarations and function declarations. // Finally we will get to the main program, which begins with the // comment `|start_here|'. If you want to skip down to the // main program now, you can look up `|start_here|' in the index. // But the author suggests that the best way to understand this program // is to follow pretty much the order of \TeX's components as they appear in the // \.[WEB] description you are now reading, since the present ordering is // intended to combine the advantages of the ``bottom up'' and ``top down'' // approaches to the problem of understanding a somewhat complicated system. // 7. // tangle:pos tex.web:290:3: // Some of the code below is intended to be used only when diagnosing the // strange behavior that sometimes occurs when \TeX\ is being installed or // when system wizards are fooling around with \TeX\ without quite knowing // what they are doing. Such code will not normally be compiled; it is // 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], // \.[\\tracingpages], and \.[\\tracingrestores]. // \xref[debugging] // 10. // tangle:pos tex.web:348:3: // This \TeX\ implementation conforms to the rules of the [\sl Pascal User // \xref[PASCAL][\PASCAL] // \xref[system dependencies] // Manual] published by Jensen and Wirth in 1975, except where system-dependent // \xref[Wirth, Niklaus] // \xref[Jensen, Kathleen] // code is necessary to make a useful system program, and except in another // respect where such conformity would unnecessarily obscure the meaning // and clutter up the code: We assume that |case| statements may include a // default case that applies if no matching label is found. Thus, we shall use // constructions like // $$\vbox[\halign[\ignorespaces#\hfil\cr // |case x of|\cr // 1: $\langle\,$code for $x=1\,\rangle$;\cr // 3: $\langle\,$code for $x=3\,\rangle$;\cr // |othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr // |endcases|\cr]]$$ // since most \PASCAL\ compilers have plugged this hole in the language by // incorporating some sort of default mechanism. For example, the \ph\ // compiler allows `|others|:' as a default label, and other \PASCAL s allow // 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 // extended by listing all remaining cases. People who are stuck with such // \PASCAL s have, in fact, done this, successfully but not happily!) // \xref[PASCAL H][\ph] // 12. // tangle:pos tex.web:430:3: // Like the preceding parameters, the following quantities can be changed // at compile time to extend or reduce \TeX's capacity. But if they are changed, // it is necessary to rerun the initialization program \.[INITEX] // \xref[INITEX] // to generate new tables for the production \TeX\ program. // One can't simply make helter-skelter changes to the following constants, // since certain rather complex initialization // numbers are computed from them. They are defined here using // \.[WEB] macros, instead of being put into \PASCAL's |const| list, in order to // emphasize this distinction. // 15. // tangle:pos tex.web:476:3: // Labels are given symbolic names by the following definitions, so that // 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|'. // // Incidentally, this program never declares a label that isn't actually used, // because some fussy \PASCAL\ compilers will complain about redundant labels. // 16. // tangle:pos tex.web:510:3: // Here are some macros for common programming idioms. // 17. \[2] The character set // tangle:pos tex.web:523:27: // 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 // Information Interchange.'' This conversion is done immediately when each // character is read in. Conversely, characters are converted from ASCII to // the user's external representation just before they are output to a // text file. // // Such an internal code is relevant to users of \TeX\ primarily because it // governs the positions of characters in the fonts. For example, the // character `\.A' has ASCII code $65=@'101$, and when \TeX\ typesets // this letter it specifies character number 65 in the current font. // If that font actually has `\.A' in a different position, \TeX\ doesn't // know what the real position is; the program that does the actual printing from // \TeX's device-independent files is responsible for converting from ASCII to // a particular font encoding. // \xref[ASCII code] // // \TeX's internal code also defines the value of constants // that begin with a reverse apostrophe; and it provides an index to the // \.[\\catcode], \.[\\mathcode], \.[\\uccode], \.[\\lccode], and \.[\\delcode] // tables. // 22. // tangle:pos tex.web:702:3: // Some of the ASCII codes without visible characters have been given symbolic // names in this program because they are used with a special meaning. // 27. // tangle:pos tex.web:808:3: // 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|, // we can write // $$\vbox[\halign[#\hfil\qquad&#\hfil\cr // |reset(f,\\[name],'/O')|&for input;\cr // |rewrite(f,\\[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. // Blank spaces that might appear in \\[name] are ignored. // // The `\.[/O]' parameter tells the operating system not to issue its own // error messages if something goes wrong. If a file of the specified name // cannot be found, or if such a file cannot be opened for some other reason // (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. // \xref[PASCAL H][\ph] // \xref[system dependencies] // // \TeX's file-opening procedures return |false| if no file identified by // |name_of_file| could be opened. func (prg *prg) aOpenIn(f alphaFile) (r bool) { f.Reset(arraystr(prg.nameOfFile[:]), "/O") r = f.ErStat() == 0 return r } func (prg *prg) aOpenOut(f alphaFile) (r bool) { f.Rewrite(arraystr(prg.nameOfFile[:]), "/O") r = f.ErStat() == 0 return r } func (prg *prg) bOpenIn(f byteFile) (r bool) { f.Reset(arraystr(prg.nameOfFile[:]), "/O") r = f.ErStat() == 0 return r } func (prg *prg) bOpenOut(f byteFile) (r bool) { f.Rewrite(arraystr(prg.nameOfFile[:]), "/O") r = f.ErStat() == 0 return r } func (prg *prg) wOpenIn(f wordFile) (r bool) { f.Reset(arraystr(prg.nameOfFile[:]), "/O") r = f.ErStat() == 0 return r } func (prg *prg) wOpenOut(f wordFile) (r bool) { f.Rewrite(arraystr(prg.nameOfFile[:]), "/O") r = f.ErStat() == 0 return r } // 28. // tangle:pos tex.web:864:3: // Files can be closed with the \ph\ routine `|close(f)|', which // \xref[PASCAL H][\ph] // \xref[system dependencies] // should be used when all input or output with respect to |f| has been completed. // This makes |f| available to be opened again, if desired; and if |f| was used for // output, the |close| operation makes the corresponding external file appear // on the user's area, ready to be read. // // These procedures should not generate error messages if a file is // being closed before it has been successfully opened. func (prg *prg) aClose(f alphaFile) { f.Close() } func (prg *prg) bClose(f byteFile) { f.Close() } func (prg *prg) wClose(f wordFile) { f.Close() } // 29. // tangle:pos tex.web:887:3: // Binary input and output are done with \PASCAL's ordinary |get| and |put| // procedures, so we don't have to make any other special arrangements for // binary~I/O. Text output is also easy to do with standard \PASCAL\ routines. // The treatment of text input is more difficult, however, because // of the necessary translation to |ASCII_code| values. // \TeX's conventions should be efficient, and they should // blend nicely with the user's operating environment. // 31. // tangle:pos tex.web:908:3: // The |input_ln| function brings the next line of input from the specified // 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]<>" "|. // // An overflow error is given, however, if the normal actions of |input_ln| // would make |last>=buf_size|; this is done so that other parts of \TeX\ // can safely look at the contents of |buffer[last+1]| without overstepping // the bounds of the |buffer| array. Upon entry to |input_ln|, the condition // |first= int32(prg.maxBufStack) { prg.maxBufStack = uint16(int32(prg.last) + 1) if int32(prg.maxBufStack) == bufSize { if int32(prg.formatIdent) == 0 { prg.termOut.Writeln("Buffer size exceeded!") panic(signal(finalEnd)) // \xref[Buffer size exceeded] } else { prg.curInput.locField = prg.first prg.curInput.limitField = uint16(int32(prg.last) - 1) prg.overflow(strNumber( /* "buffer size" */ 256), bufSize) // \xref[TeX capacity exceeded buffer size][\quad buffer size] } } } prg.buffer[prg.last] = prg.xord[*f.ByteP()] f.Get() prg.last = uint16(int32(prg.last) + 1) if int32(prg.buffer[int32(prg.last)-1]) != ' ' { lastNonblank = prg.last } } prg.last = lastNonblank r = true } return r } // 33. // tangle:pos tex.web:979:3: // Here is how to open the terminal files // in \ph. The `\.[/I]' switch suppresses the first |get|. // \xref[PASCAL H][\ph] // \xref[system dependencies] // 34. // tangle:pos tex.web:987:3: // Sometimes it is necessary to synchronize the input/output mixture that // happens on the user's terminal, and three system-dependent // procedures are used for this // purpose. The first of these, |update_terminal|, is called when we want // to make sure that everything we have output to the terminal so far has // actually left the computer's internal buffers and been sent. // The second, |clear_terminal|, is called when we wish to cancel any // input that the user may have typed ahead (since we are about to // 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: // \xref[PASCAL H][\ph] // \xref[system dependencies] // 36. // tangle:pos tex.web:1044:3: // Different systems have different ways to get started. But regardless of // what conventions are adopted, the routine that initializes the terminal // should satisfy the following specifications: // // \yskip\textindent[1)]It should open file |term_in| for input from the // terminal. (The file |term_out| will already be open for output to the // terminal.) // // \textindent[2)]If the user has given a command line, this line should be // 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. // // \textindent[3)]The first line of input, which might or might not be a // command line, should appear in locations |first| to |last-1| of the // |buffer| array. // // \textindent[4)]The global variable |loc| should be set so that the // character to be read next by \TeX\ is in |buffer[loc]|. This // character should not be blank, and we should have |loc '~' { { prg.strPool[prg.poolPtr] = '^' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } { prg.strPool[prg.poolPtr] = '^' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } if int32(k) < 0100 { prg.strPool[prg.poolPtr] = byte(int32(k) + 0100) prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } else if int32(k) < 0200 { prg.strPool[prg.poolPtr] = byte(int32(k) - 0100) prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } else { l = byte(int32(k) / 16) if int32(l) < 10 { prg.strPool[prg.poolPtr] = byte(int32(l) + '0') prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } else { prg.strPool[prg.poolPtr] = byte(int32(l) - 10 + 'a') prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } l = byte(int32(k) % 16) if int32(l) < 10 { prg.strPool[prg.poolPtr] = byte(int32(l) + '0') prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } else { prg.strPool[prg.poolPtr] = byte(int32(l) - 10 + 'a') prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } } } else { prg.strPool[prg.poolPtr] = k prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } g = prg.makeString() } // Read the other strings from the \.[TEX.POOL] file and return |true|, or give an error message and return |false| strcopy(prg.nameOfFile[:], "TeXformats:TEX.POOL ") // we needn't set |name_length| if prg.aOpenIn(prg.poolFile) { c = false for { // Read one string, but return |false| if the string memory space is getting too tight for comfort { if prg.poolFile.EOF() { prg.termOut.Writeln("! TEX.POOL has no check sum.") prg.aClose(prg.poolFile) r = false goto exit } // \xref[TEX.POOL has no check sum] prg.poolFile.Read(&m, &n) // read two digits of string length if int32(m) == '*' { a = 0 k = 1 for true { if int32(prg.xord[n]) < '0' || int32(prg.xord[n]) > '9' { prg.termOut.Writeln("! TEX.POOL check sum doesn't have nine digits.") prg.aClose(prg.poolFile) r = false goto exit } // \xref[TEX.POOL check sum...] a = 10*a + int32(prg.xord[n]) - '0' if int32(k) == 9 { goto done } k = byte(int32(k) + 1) prg.poolFile.Read(&n) } done: if a != 504454778 { prg.termOut.Writeln("! TEX.POOL doesn't match; TANGLE me again.") prg.aClose(prg.poolFile) r = false goto exit } // \xref[TEX.POOL doesn't match] c = true } else { if int32(prg.xord[m]) < '0' || int32(prg.xord[m]) > '9' || int32(prg.xord[n]) < '0' || int32(prg.xord[n]) > '9' { prg.termOut.Writeln("! TEX.POOL line doesn't begin with two digits.") prg.aClose(prg.poolFile) r = false goto exit } // \xref[TEX.POOL line doesn't...] l = byte(int32(prg.xord[m])*10 + int32(prg.xord[n]) - '0'*11) // compute the length if int32(prg.poolPtr)+int32(l)+stringVacancies > poolSize { prg.termOut.Writeln("! You have to increase POOLSIZE.") prg.aClose(prg.poolFile) r = false goto exit } // \xref[You have to increase POOLSIZE] for ii := int32(1); ii <= int32(l); ii++ { k = byte(ii) _ = k if prg.poolFile.EOLN() { m = ' ' } else { prg.poolFile.Read(&m) } { prg.strPool[prg.poolPtr] = prg.xord[m] prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } } prg.poolFile.Readln() g = prg.makeString() } } if c { break } } prg.aClose(prg.poolFile) r = true } else { prg.termOut.Writeln("! I can't read TEX.POOL.") prg.aClose(prg.poolFile) r = false goto exit } exit: ; return r } // 56. // tangle:pos tex.web:1448:3: // 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. // \xref[system dependencies] // 66. // tangle:pos tex.web:1638:3: // Here is a trivial procedure to print two digits; it is usually called with // a parameter in the range |0<=n<=99|. func (prg *prg) printTwo(n int32) { n = abs(n) % 100 prg.printChar(asciiCode('0' + n/10)) prg.printChar(asciiCode('0' + n%10)) } // 67. // tangle:pos tex.web:1646:3: // Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. func (prg *prg) printHex(n int32) { // prints a positive integer in hexadecimal form var ( k /* 0..22 */ byte // index to current digit; we assume that $0\L n<16^[22]$ ) k = 0 prg.printChar(asciiCode('"')) for { prg.dig[k] = byte(n % 16) n = n / 16 k = byte(int32(k) + 1) if n == 0 { break } } prg.printTheDigs(k) } // 68. // tangle:pos tex.web:1657:3: // 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. // 69. // tangle:pos tex.web:1663:3: // 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]. func (prg *prg) printRomanInt(n int32) { var ( j, k poolPointer // mysterious indices into |str_pool| u, v nonnegativeInteger // mysterious numbers ) j = prg.strStart[ /* "m2d5c2l5x2v5i" */ 260] v = 1000 for true { for n >= int32(v) { prg.printChar(prg.strPool[j]) n = n - int32(v) } if n <= 0 { goto exit } // nonpositive input produces no output k = uint16(int32(j) + 2) u = uint32(int32(v) / (int32(prg.strPool[int32(k)-1]) - '0')) if int32(prg.strPool[int32(k)-1]) == '2' { k = uint16(int32(k) + 2) u = uint32(int32(u) / (int32(prg.strPool[int32(k)-1]) - '0')) } if n+int32(u) >= int32(v) { prg.printChar(prg.strPool[k]) n = n + int32(u) } else { j = uint16(int32(j) + 2) v = uint32(int32(v) / (int32(prg.strPool[int32(j)-1]) - '0')) } } exit: } // 70. // tangle:pos tex.web:1689:3: // The |print| subroutine will not print a string that is still being // created. The following procedure will. func (prg *prg) printCurrentString() { // prints a yet-unmade string var ( j poolPointer // points to current character code ) j = prg.strStart[prg.strPtr] for int32(j) < int32(prg.poolPtr) { prg.printChar(prg.strPool[j]) j = uint16(int32(j) + 1) } } // \2 func (prg *prg) termInput() { // gets a line from the terminal var ( k /* 0..bufSize */ uint16 // index into |buffer| ) // now the user sees the prompt for sure if !prg.inputLn(prg.termIn, true) { prg.fatalError(strNumber( /* "End of file on the terminal!" */ 261)) } // \xref[End of file on the terminal] prg.termOffset = 0 // the user's line ended with \<\rm return> prg.selector = byte(int32(prg.selector) - 1) // prepare to echo the input if int32(prg.last) != int32(prg.first) { for ii := int32(prg.first); ii <= int32(prg.last)-1; ii++ { k = uint16(ii) _ = k prg.print(int32(prg.buffer[k])) } } prg.printLn() prg.selector = byte(int32(prg.selector) + 1) // restore previous status } // 72. \[6] Reporting errors // tangle:pos tex.web:1721:26: // When something anomalous is detected, \TeX\ typically does something like this: // $$\vbox[\halign[#\hfil\cr // |print_err("Something anomalous has been detected");|\cr // |help3("This is the first line of my offer to help.")|\cr // |("This is the second line. I'm trying to")|\cr // |("explain the best way for you to proceed.");|\cr // |error;|\cr]]$$ // A two-line help message would be given using |help2|, etc.; these informal // helps should use simple vocabulary that complements the words used in the // official error message that was printed. (Outside the U.S.A., the help // messages should preferably be translated into the local vernacular. Each // line of help is at most 60 characters long, in the present implementation, // so that |max_print_line| will not be exceeded.) // // 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. // The |error| procedure supplies a `\..' after the official message, then it // shows the location of the error; and if |interaction=error_stop_mode|, // it also enters into a dialog with the user, during which time the help // message may be printed. // \xref[system dependencies] // 91. // tangle:pos tex.web:2034:3: // A dozen or so error messages end with a parenthesized integer, so we // save a teeny bit of program space by declaring the following procedure: func (prg *prg) intError(n int32) { prg.print( /* " (" */ 286) prg.printInt(n) prg.printChar(asciiCode(')')) prg.error1() } // \4 // Error handling procedures func (prg *prg) normalizeSelector() { if prg.logOpened { prg.selector = byte(termAndLog) } else { prg.selector = byte(termOnly) } if int32(prg.jobName) == 0 { prg.openLogFile() } if int32(prg.interaction) == batchMode { prg.selector = byte(int32(prg.selector) - 1) } } // 98. // tangle:pos tex.web:2124:3: // When an interrupt has been detected, the program goes into its // highest interaction level and lets the user have nearly the full flexibility of // the |error| routine. \TeX\ checks for interrupts only at times when it is // safe to do this. func (prg *prg) pauseForInstructions() { if prg.okToInterrupt { prg.interaction = byte(errorStopMode) if int32(prg.selector) == logOnly || int32(prg.selector) == noPrint { prg.selector = byte(int32(prg.selector) + 1) } { if int32(prg.interaction) == errorStopMode { } prg.printNl(strNumber( /* "! " */ 262)) prg.print( /* "Interruption" */ 296) } // \xref[Interruption] { prg.helpPtr = 3 prg.helpLine[2] = /* "You rang?" */ 297 prg.helpLine[1] = /* "Try to insert an instruction for me (e.g., `I\\showlists')," */ 298 prg.helpLine[0] = /* "unless you just want to quit by typing `X'." */ 299 } prg.deletionsAllowed = false prg.error1() prg.deletionsAllowed = true prg.interrupt = 0 } } // 99. \[7] Arithmetic with scaled dimensions // tangle:pos tex.web:2144:43: // The principal computations performed by \TeX\ are done entirely in terms of // integers less than $2^[31]$ in magnitude; and divisions are done only when both // dividend and divisor are nonnegative. Thus, the arithmetic specified in this // program can be carried out in exactly the same way on a wide variety of // computers, including some small ones. Why? Because the arithmetic // calculations need to be spelled out precisely in order to guarantee that // \TeX\ will produce identical output on different machines. If some // quantities were rounded differently in different implementations, we would // find that line breaks and even page breaks might occur in different places. // Hence the arithmetic of \TeX\ has been designed with care, and systems that // claim to be implementations of \TeX82 should follow precisely the // \xref[TeX82][\TeX82] // calculations as they appear in the present program. // // (Actually there are three places where \TeX\ uses |div| with a possibly negative // 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|.) // 100. // tangle:pos tex.web:2165:3: // Here is a routine that calculates half of an integer, using an // unambiguous convention with respect to signed odd numbers. func (prg *prg) half(x int32) (r int32) { if x&1 != 0 { r = (x + 1) / 2 } else { r = x / 2 } return r } // 102. // tangle:pos tex.web:2185:3: // 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 // given in |dig[i]|, and the calculation produces a correctly rounded result. func (prg *prg) roundDecimals(k smallNumber) (r scaled) { // converts a decimal fraction var ( a int32 // the accumulator ) a = 0 for int32(k) > 0 { k = byte(int32(k) - 1) a = (a + int32(prg.dig[k])*0400000) / 10 } r = (a + 1) / 2 return r } // 103. // tangle:pos tex.web:2199:3: // Conversely, here is a procedure analogous to |print_int|. If the output // of this procedure is subsequently read by \TeX\ and converted by the // |round_decimals| routine above, it turns out that the original value will // 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 0200000 { s = s + 0100000 - 50000 } // round the last digit prg.printChar(asciiCode('0' + s/0200000)) s = 10 * (s % 0200000) delta = delta * 10 if s <= delta { break } } } // 105. // tangle:pos tex.web:2254:3: // The first arithmetical subroutine we need computes $nx+y$, where |x| // and~|y| are |scaled| and |n| is an integer. We will also use it to // multiply integers. func (prg *prg) multAndAdd(n int32, x, y, maxAnswer scaled) (r scaled) { if n < 0 { x = -x n = -n } if n == 0 { r = y } else if x <= (maxAnswer-y)/n && -x <= (maxAnswer+y)/n { r = n*x + y } else { prg.arithError = true r = 0 } return r } // 106. // tangle:pos tex.web:2272:3: // We also need to divide scaled dimensions by integers. func (prg *prg) xOverN(x scaled, n int32) (r scaled) { var ( negative bool // should |remainder| be negated? ) negative = false if n == 0 { prg.arithError = true r = 0 prg.remainder = x } else { if n < 0 { x = -x n = -n negative = true } if x >= 0 { r = x / n prg.remainder = x % n } else { r = -(-x / n) prg.remainder = -(-x % n) } } if negative { prg.remainder = -prg.remainder } return r } // 107. // tangle:pos tex.web:2292:3: // Then comes the multiplication of a scaled number by a fraction |n/d|, // where |n| and |d| are nonnegative integers |<=$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 // would be too inaccurate to divide by |d| and then multiply by |n|. Hence // this subroutine simulates 1.5-precision arithmetic. func (prg *prg) xnOverD(x scaled, n, d int32) (r scaled) { var ( positive bool // was |x>=0|? t, u, v nonnegativeInteger // intermediate quantities ) if x >= 0 { positive = true } else { x = -x positive = false } t = uint32(x % 0100000 * n) u = uint32(x/0100000*n + int32(t)/0100000) v = uint32(int32(u)%d*0100000 + int32(t)%0100000) if int32(u)/d >= 0100000 { prg.arithError = true } else { u = uint32(0100000*(int32(u)/d) + int32(v)/d) } if positive { r = int32(u) prg.remainder = int32(v) % d } else { r = -int32(u) prg.remainder = -(int32(v) % d) } return r } // 108. // tangle:pos tex.web:2317:3: // The next subroutine is used to compute the “badness” of glue, when a // 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$; // however, badness is simply a heuristic, so we need not squeeze out the // last drop of accuracy when computing it. All we really want is an // approximation that has similar properties. // \xref[TeXbook][\sl The \TeX book] // // The actual method used to compute the badness is easier to read from the // program than to describe in words. It produces an integer value that is a // reasonably close approximation to $100(t/s)^3$, and all implementations // of \TeX\ should use precisely this method. Any badness of $2^[13]$ or more is // treated as infinitely bad, and represented by 10000. // // 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 // computing at most 1095 distinct values, but that is plenty. func (prg *prg) badness(t, s scaled) (r halfword) { // compute badness, given |t>=0| var ( r1 int32 // approximation to $\alpha t/s$, where $\alpha^3\approx // 100\cdot2^[18]$ ) if t == 0 { r = 0 } else if s <= 0 { r = uint16(infBad) } else { if t <= 7230584 { r1 = t * 297 / s } else if s >= 1663497 { r1 = t / (s / 297) } else { r1 = t } if r1 > 1290 { r = uint16(infBad) } else { r = uint16((r1*r1*r1 + 0400000) / 01000000) } } // that was $r^3/2^[18]$, rounded to the nearest integer return r } // 110. \[8] Packed data // tangle:pos tex.web:2375:21: // In order to make efficient use of storage space, \TeX\ bases its major data // structures on a |memory_word|, which contains either a (signed) integer, // possibly scaled, or a (signed) |glue_ratio|, or a small number of // fields that are one half or one quarter of the size used for storing // integers. // // If |x| is a variable of type |memory_word|, it contains up to four // fields that can be referred to as follows: // $$\vbox[\halign[\hfil#&#\hfil&#\hfil\cr // |x|&.|int|&(an |integer|)\cr // |x|&.|sc|\qquad&(a |scaled| integer)\cr // |x|&.|gr|&(a |glue_ratio|)\cr // |x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr // |x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword // field)\cr // |x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt // &\qquad\qquad\qquad(four quarterword fields)\cr]]$$ // This is somewhat cumbersome to write, and not very readable either, but // macros will be used to make the notation shorter and more transparent. // The \PASCAL\ code below gives a formal definition of |memory_word| and // its subsidiary types, using packed variant records. \TeX\ makes no // assumptions about the relative positions of the fields within a word. // // Since we are assuming 32-bit integers, a halfword must contain at least // 16 bits, and a quarterword must contain at least 8 bits. // \xref[system dependencies] // But it doesn't hurt to have more bits; for example, with enough 36-bit // words you might be able to have |mem_max| as large as 262142, which is // eight times as much memory as anybody had during the first four years of // \TeX's existence. // // N.B.: Valuable memory space will be dreadfully wasted unless \TeX\ is compiled // by a \PASCAL\ that packs all of the |memory_word| variants into // the space of a single integer. This means, for example, that |glue_ratio| // words should be |short_real| instead of |real| on some computers. Some // \PASCAL\ compilers will pack an integer whose subrange is `|0..255|' into // an eight-bit field, but others insist on allocating space for an additional // sign bit; on such systems you can get 256 values into a quarterword only // if the subrange is `|-128..127|'. // // The present implementation tries to accommodate as many variations as possible, // so it makes few assumptions. If integers having the subrange // `|min_quarterword..max_quarterword|' can be packed into a quarterword, // and if integers having the subrange `|min_halfword..max_halfword|' // can be packed into a halfword, everything should work satisfactorily. // // It is usually most efficient to have |min_quarterword=min_halfword=0|, // so one should try to achieve this unless it causes a severe problem. // The values defined here are recommended for most 32-bit computers. // 112. // tangle:pos tex.web:2449:3: // The operation of adding or subtracting |min_quarterword| occurs quite // frequently in \TeX, so it is convenient to abbreviate this operation // by using the macros |qi| and |qo| for input and output to and from // quarterword format. // // The inner loop of \TeX\ will run faster with respect to compilers // that don't optimize expressions like `|x+0|' and `|x-0|', if these // macros are simplified in the obvious way when |min_quarterword=0|. // \xref[inner loop]\xref[system dependencies] // 114. // tangle:pos tex.web:2499:3: // When debugging, we may want to print a |memory_word| without knowing // what type it is; so we print it in all modes. // \xref[dirty \PASCAL]\xref[debugging] // procedure print_word( w:memory_word); // [prints |w| in all ways] // begin print_int(w.int); print_char([" "=]32); // // print_scaled(w.int ); print_char([" "=]32); // // print_scaled(round( [0200000=]65536 * w. gr )); print_ln; // // [ \xref[real multiplication] ] // print_int(w.hh.lh); print_char(["="=]61); print_int(w.hh.b0); print_char([":"=]58); // print_int(w.hh.b1); print_char([";"=]59); print_int(w.hh.rh); print_char([" "=]32); // // print_int(w.qqqq.b0); print_char([":"=]58); print_int(w.qqqq.b1); print_char([":"=]58); // print_int(w.qqqq.b2); print_char([":"=]58); print_int(w.qqqq.b3); // end; // [ ] // 119. // tangle:pos tex.web:2596:3: // If memory is exhausted, it might mean that the user has forgotten // a right brace. We will define some procedures later that try to help // pinpoint the trouble. // Declare the procedure called |show_token_list| func (prg *prg) showTokenList(p, q int32, l int32) { var ( m, c int32 // pieces of a token matchChr asciiCode // character used in a `|match|' n asciiCode // the highest parameter number, as an ASCII digit ) matchChr = '#' n = '0' prg.tally = 0 for p != 0 && prg.tally < l { if p == q { prg.firstCount = prg.tally prg.trickCount = prg.tally + 1 + errorLine - halfErrorLine if prg.trickCount < errorLine { prg.trickCount = errorLine } } // Display token |p|, and |return| if there are problems if p < int32(prg.hiMemMin) || p > int32(prg.memEnd) { prg.printEsc(strNumber( /* "CLOBBERED." */ 309)) goto exit // \xref[CLOBBERED] } if int32(*(*prg.mem[p].hh()).lh()) >= 07777 { prg.printCs(int32(*(*prg.mem[p].hh()).lh()) - 07777) } else { m = int32(*(*prg.mem[p].hh()).lh()) / 0400 c = int32(*(*prg.mem[p].hh()).lh()) % 0400 if int32(*(*prg.mem[p].hh()).lh()) < 0 { prg.printEsc(strNumber( /* "BAD." */ 555)) } else { // Display the token $(|m|,|c|)$ switch m { case leftBrace, rightBrace, mathShift, tabMark, supMark, subMark, spacer, letter, otherChar: prg.print(c) case macParam: prg.print(c) prg.print(c) case outParam: prg.print(int32(matchChr)) if c <= 9 { prg.printChar(asciiCode(c + '0')) } else { prg.printChar(asciiCode('!')) goto exit } case match: matchChr = byte(c) prg.print(c) n = byte(int32(n) + 1) prg.printChar(n) if int32(n) > '9' { goto exit } case endMatch: prg.print( /* "->" */ 556) // \xref[->] default: prg.printEsc(strNumber( /* "BAD." */ 555)) // \xref[BAD] } } } p = int32(*(*prg.mem[p].hh()).rh()) } if p != 0 { prg.printEsc(strNumber( /* "ETC." */ 554)) } // \xref[ETC] // \xref[ETC] exit: } // Declare the procedure called |runaway| func (prg *prg) runaway() { var ( p halfword // head of runaway list ) if int32(prg.scannerStatus) > skipping { prg.printNl(strNumber( /* "Runaway " */ 569)) // \xref[Runaway...] switch prg.scannerStatus { case defining: prg.print( /* "definition" */ 570) p = prg.defRef case matching: prg.print( /* "argument" */ 571) p = uint16(30000 - 3) case aligning: prg.print( /* "preamble" */ 572) p = uint16(30000 - 4) case absorbing: prg.print( /* "text" */ 573) p = prg.defRef } // there are no other cases prg.printChar(asciiCode('?')) prg.printLn() prg.showTokenList(int32(*(*prg.mem[p].hh()).rh()), 0, errorLine-10) } } // 120. // tangle:pos tex.web:2603:3: // The function |get_avail| returns a pointer to a new one-word node whose // |link| field is null. However, \TeX\ will halt if there is no more room left. // \xref[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. func (prg *prg) getAvail() (r halfword) { // single-word node allocation var ( p halfword // the new node being got ) p = prg.avail // get top location in the |avail| stack if int32(p) != 0 { prg.avail = *(*prg.mem[prg.avail].hh()).rh() } else if int32(prg.memEnd) < memMax { prg.memEnd = uint16(int32(prg.memEnd) + 1) p = prg.memEnd } else { prg.hiMemMin = uint16(int32(prg.hiMemMin) - 1) p = prg.hiMemMin if int32(prg.hiMemMin) <= int32(prg.loMemMax) { prg.runaway() // if memory is exhausted, display possible runaway text prg.overflow(strNumber( /* "main memory size" */ 300), memMax+1-memMin) // quit; all one-word nodes are busy // \xref[TeX capacity exceeded main memory size][\quad main memory size] } } *(*prg.mem[p].hh()).rh() = 0 // provide an oft-desired initialization of the new node // dyn_used:= dyn_used+1 ; [ ] // // maintain statistics r = p return r } // 121. // tangle:pos tex.web:2632:3: // Conversely, a one-word node is recycled by calling |free_avail|. // This routine is part of \TeX's ``inner loop,'' so we want it to be fast. // \xref[inner loop] // 122. // tangle:pos tex.web:2641:3: // 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|. // \xref[inner loop] // 123. // tangle:pos tex.web:2654:3: // The procedure |flush_list(p)| frees an entire linked list of // one-word nodes that starts at position |p|. // \xref[inner loop] func (prg *prg) flushList(p halfword) { // makes list of single-word nodes // available var ( q, r1 halfword // list traversers ) if int32(p) != 0 { r1 = p for { q = r1 r1 = *(*prg.mem[r1].hh()).rh() // dyn_used:= dyn_used-1 ; [ ] if int32(r1) == 0 { break } } // now |q| is the last node on the list *(*prg.mem[q].hh()).rh() = prg.avail prg.avail = p } } // 125. // tangle:pos tex.web:2694:3: // A call to |get_node| with argument |s| returns a pointer to a new node // of size~|s|, which must be 2~or more. The |link| field of the first word // of this new node is set to null. An overflow stop occurs if no suitable // space exists. // // If |get_node| is called with $s=2^[30]$, it simply merges adjacent free // areas and returns the value |max_halfword|. func (prg *prg) getNode(s int32) (r halfword) { var ( p halfword // the node currently under inspection q halfword // the node physically after node |p| r1 int32 // the newly allocated node, or a candidate for this honor t int32 // temporary register ) restart: p = prg.rover // start at some free node in the ring for { // Try to allocate within node |p| and its physical successors, and |goto found| if allocation was possible q = uint16(int32(p) + int32(*(*prg.mem[p].hh()).lh())) // find the physical successor // \xref[inner loop] for int32(*(*prg.mem[q].hh()).rh()) == 65535 { // merge node |p| with node |q| t = int32(*(*prg.mem[int32(q)+1].hh()).rh()) if int32(q) == int32(prg.rover) { prg.rover = uint16(t) } *(*prg.mem[t+1].hh()).lh() = *(*prg.mem[int32(q)+1].hh()).lh() *(*prg.mem[int32(*(*prg.mem[int32(q)+1].hh()).lh())+1].hh()).rh() = uint16(t) q = uint16(int32(q) + int32(*(*prg.mem[q].hh()).lh())) } r1 = int32(q) - s if r1 > int32(p)+1 { *(*prg.mem[p].hh()).lh() = uint16(r1 - int32(p)) // store the remaining size // \xref[inner loop] prg.rover = p // start searching here next time // start searching here next time goto found } if r1 == int32(p) { if int32(*(*prg.mem[int32(p)+1].hh()).rh()) != int32(p) { prg.rover = *(*prg.mem[int32(p)+1].hh()).rh() t = int32(*(*prg.mem[int32(p)+1].hh()).lh()) *(*prg.mem[int32(prg.rover)+1].hh()).lh() = uint16(t) *(*prg.mem[t+1].hh()).rh() = prg.rover goto found } } *(*prg.mem[p].hh()).lh() = uint16(int32(q) - int32(p)) // \xref[inner loop] p = *(*prg.mem[int32(p)+1].hh()).rh() // move to the next node in the ring if int32(p) == int32(prg.rover) { break } } // repeat until the whole list has been traversed if s == 010000000000 { r = 65535 goto exit } if int32(prg.loMemMax)+2 < int32(prg.hiMemMin) { if int32(prg.loMemMax)+2 <= memBot+65535 { if int32(prg.hiMemMin)-int32(prg.loMemMax) >= 1998 { t = int32(prg.loMemMax) + 1000 } else { t = int32(prg.loMemMax) + 1 + (int32(prg.hiMemMin)-int32(prg.loMemMax))/2 } // |lo_mem_max+2<=t memBot+65535 { t = memBot + 65535 } *(*prg.mem[int32(q)+1].hh()).rh() = prg.rover *(*prg.mem[int32(q)+1].hh()).lh() = p *(*prg.mem[q].hh()).rh() = 65535 *(*prg.mem[q].hh()).lh() = uint16(t - int32(prg.loMemMax)) prg.loMemMax = uint16(t) *(*prg.mem[prg.loMemMax].hh()).rh() = 0 *(*prg.mem[prg.loMemMax].hh()).lh() = 0 prg.rover = q goto restart } } prg.overflow(strNumber( /* "main memory size" */ 300), memMax+1-memMin) // sorry, nothing satisfactory is left // \xref[TeX capacity exceeded main memory size][\quad main memory size] // sorry, nothing satisfactory is left // \xref[TeX capacity exceeded main memory size][\quad main memory size] found: *(*prg.mem[r1].hh()).rh() = 0 // this node is now nonempty // var_used:=var_used+s; [maintain usage statistics] // [ ] r = uint16(r1) exit: ; return r } // 130. // tangle:pos tex.web:2780:3: // Conversely, when some variable-size node |p| of size |s| is no longer needed, // the operation |free_node(p,s)| will make its words available, by inserting // |p| as a new empty node just before where |rover| now points. // \xref[inner loop] func (prg *prg) freeNode(p halfword, s halfword) { // variable-size node // liberation var ( q halfword // |llink(rover)| ) *(*prg.mem[p].hh()).lh() = s *(*prg.mem[p].hh()).rh() = 65535 q = *(*prg.mem[int32(prg.rover)+1].hh()).lh() *(*prg.mem[int32(p)+1].hh()).lh() = q *(*prg.mem[int32(p)+1].hh()).rh() = prg.rover // set both links *(*prg.mem[int32(prg.rover)+1].hh()).lh() = p *(*prg.mem[int32(q)+1].hh()).rh() = p // insert |p| into the ring // var_used:=var_used-s; [ ] // maintain statistics } // 131. // tangle:pos tex.web:2794:3: // Just before \.[INITEX] writes out the memory, it sorts the doubly linked // available space list. The list is probably very short at such times, so a // simple insertion sort is used. The smallest available location will be // pointed to by |rover|, the next-smallest by |rlink(rover)|, etc. func (prg *prg) sortAvail() { // sorts the available variable-size nodes // by location var ( p, q, r1 halfword // indices into |mem| oldRover halfword // initial |rover| setting ) p = prg.getNode(010000000000) // merge adjacent free areas p = *(*prg.mem[int32(prg.rover)+1].hh()).rh() *(*prg.mem[int32(prg.rover)+1].hh()).rh() = 65535 oldRover = prg.rover for int32(p) != int32(oldRover) { // Sort \(p)|p| into the list starting at |rover| and advance |p| to |rlink(p)| if int32(p) < int32(prg.rover) { q = p p = *(*prg.mem[int32(q)+1].hh()).rh() *(*prg.mem[int32(q)+1].hh()).rh() = prg.rover prg.rover = q } else { q = prg.rover for int32(*(*prg.mem[int32(q)+1].hh()).rh()) < int32(p) { q = *(*prg.mem[int32(q)+1].hh()).rh() } r1 = *(*prg.mem[int32(p)+1].hh()).rh() *(*prg.mem[int32(p)+1].hh()).rh() = *(*prg.mem[int32(q)+1].hh()).rh() *(*prg.mem[int32(q)+1].hh()).rh() = p p = r1 } } p = prg.rover for int32(*(*prg.mem[int32(p)+1].hh()).rh()) != 65535 { *(*prg.mem[int32(*(*prg.mem[int32(p)+1].hh()).rh())+1].hh()).lh() = p p = *(*prg.mem[int32(p)+1].hh()).rh() } *(*prg.mem[int32(p)+1].hh()).rh() = prg.rover *(*prg.mem[int32(prg.rover)+1].hh()).lh() = p } // 133. \[10] Data structures for boxes and their friends // tangle:pos tex.web:2828:54: // From the computer's standpoint, \TeX's chief mission is to create // horizontal and vertical lists. We shall now investigate how the elements // of these lists are represented internally as nodes in the dynamic memory. // // A horizontal or vertical list is linked together by |link| fields in // the first word of each node. Individual nodes represent boxes, glue, // penalties, or special things like discretionary hyphens; because of this // variety, some nodes are longer than others, and we must distinguish different // kinds of nodes. We do this by putting a `|type|' field in the first word, // together with the link and an optional `|subtype|'. // 134. // tangle:pos tex.web:2843:3: // A | char_node|, which represents a single character, is the most important // kind of node because it accounts for the vast majority of all boxes. // Special precautions are therefore taken to ensure that a |char_node| does // not take up much memory space. Every such node is one word long, and in fact // it is identifiable by this property, since other kinds of nodes have at least // two words, and they appear in |mem| locations less than |hi_mem_min|. // This makes it possible to omit the |type| field in a |char_node|, leaving // us room for two bytes that identify a |font| and a |character| within // that font. // // Note that the format of a |char_node| allows for up to 256 different // fonts and up to 256 characters per font; but most implementations will // probably limit the total number of fonts to fewer than 75 per job, // and most fonts will stick to characters whose codes are // less than 128 (since higher codes // are more difficult to access on most keyboards). // // Extensions of \TeX\ intended for oriental languages will need even more // than $256\times256$ possible characters, when we consider different sizes // \xref[oriental characters]\xref[Chinese characters]\xref[Japanese characters] // and styles of type. It is suggested that Chinese and Japanese fonts be // handled by representing such characters in two consecutive |char_node| // 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. // The saving feature about oriental characters is that most of them have // 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. // // In order to make sure that the |character| code fits in a quarterword, // \TeX\ adds the quantity |min_quarterword| to the actual code. // // Character nodes appear only in horizontal lists, never in vertical lists. // 135. // tangle:pos tex.web:2885:3: // An |hlist_node| stands for a box that was made from a horizontal list. // Each |hlist_node| is seven words long, and contains the following fields // (in addition to the mandatory |type| and |link|, which we shall not // mention explicitly when discussing the other node types): The |height| and // |width| and |depth| are scaled integers denoting the dimensions of the // box. There is also a |shift_amount| field, a scaled integer indicating // how much this box should be lowered (if it appears in a horizontal list), // or how much it should be moved to the right (if it appears in a vertical // list). There is a |list_ptr| field, which points to the beginning of the // list from which this box was fabricated; if |list_ptr| is |null|, the box // is empty. Finally, there are three fields that represent the setting of // the glue: |glue_set(p)| is a word of type |glue_ratio| that represents // the proportionality constant for glue setting; |glue_sign(p)| is // |stretching| or |shrinking| or |normal| depending on whether or not the // glue should stretch or shrink or remain rigid; and |glue_order(p)| // specifies the order of infinity to which glue setting applies (|normal|, // |fil|, |fill|, or |filll|). The |subtype| field is not used. // 136. // tangle:pos tex.web:2923:3: // The |new_null_box| function returns a pointer to an |hlist_node| in // which all subfields have the values corresponding to `\.[\\hbox\[\]]'. // (The |subtype| field is set to |min_quarterword|, for historic reasons // that are no longer relevant.) func (prg *prg) newNullBox() (r halfword) { // creates a new box node var ( p halfword // the new node ) p = prg.getNode(boxNodeSize) *(*prg.mem[p].hh()).b0() = byte(hlistNode) *(*prg.mem[p].hh()).b1() = byte(minQuarterword) *prg.mem[int32(p)+widthOffset].int() = 0 *prg.mem[int32(p)+depthOffset].int() = 0 *prg.mem[int32(p)+heightOffset].int() = 0 *prg.mem[int32(p)+4].int() = 0 *(*prg.mem[int32(p)+listOffset].hh()).rh() = 0 *(*prg.mem[int32(p)+listOffset].hh()).b0() = byte(normal) *(*prg.mem[int32(p)+listOffset].hh()).b1() = byte(normal) *prg.mem[int32(p)+glueOffset].gr() = float32(0.0) r = p return r } // 137. // tangle:pos tex.web:2937:3: // A |vlist_node| is like an |hlist_node| in all respects except that it // contains a vertical list. // 138. // tangle:pos tex.web:2942:3: // A |rule_node| stands for a solid black rectangle; it has |width|, // |depth|, and |height| fields just as in an |hlist_node|. However, if // any of these dimensions is $-2^[30]$, the actual value will be determined // by running the rule up to the boundary of the innermost enclosing box. // This is called a ``running dimension.'' The |width| is never running in // an hlist; the |height| and |depth| are never running in a~vlist. // 139. // tangle:pos tex.web:2954:3: // A new rule node is delivered by the |new_rule| function. It // makes all the dimensions “running,” so you have to change the // ones that are not allowed to run. func (prg *prg) newRule() (r halfword) { var ( p halfword // the new node ) p = prg.getNode(ruleNodeSize) *(*prg.mem[p].hh()).b0() = byte(ruleNode) *(*prg.mem[p].hh()).b1() = 0 // the |subtype| is not used *prg.mem[int32(p)+widthOffset].int() = -010000000000 *prg.mem[int32(p)+depthOffset].int() = -010000000000 *prg.mem[int32(p)+heightOffset].int() = -010000000000 r = p return r } // 140. // tangle:pos tex.web:2966:3: // Insertions are represented by |ins_node| records, where the |subtype| // indicates the corresponding box number. For example, `\.[\\insert 250]' // leads to an |ins_node| whose |subtype| is |250+min_quarterword|. // The |height| field of an |ins_node| is slightly misnamed; it actually holds // the natural height plus depth of the vertical list being inserted. // 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. // 141. // tangle:pos tex.web:2984:3: // A |mark_node| has a |mark_ptr| field that points to the reference count // of a token list that contains the user's \.[\\mark] text. // This field occupies a full word instead of a halfword, because // there's nothing to put in the other halfword; it is easier in \PASCAL\ to // use the full word than to risk leaving garbage in the unused half. // 142. // tangle:pos tex.web:2994:3: // 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. // 143. // tangle:pos tex.web:3003:3: // 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. // 144. // tangle:pos tex.web:3021:3: // The |new_ligature| function creates a ligature node having given // 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. func (prg *prg) newLigature(f, c quarterword, q halfword) (r halfword) { var ( p halfword // the new node ) p = prg.getNode(smallNodeSize) *(*prg.mem[p].hh()).b0() = byte(ligatureNode) *(*prg.mem[int32(p)+1].hh()).b0() = f *(*prg.mem[int32(p)+1].hh()).b1() = c *(*prg.mem[int32(p)+1].hh()).rh() = q *(*prg.mem[p].hh()).b1() = 0 r = p return r } func (prg *prg) newLigItem(c quarterword) (r halfword) { var ( p halfword // the new node ) p = prg.getNode(smallNodeSize) *(*prg.mem[p].hh()).b1() = c *(*prg.mem[int32(p)+1].hh()).rh() = 0 r = p return r } // 145. // tangle:pos tex.web:3040:3: // A |disc_node|, which occurs only in horizontal lists, specifies a // “dis\-cretion\-ary” line break. If such a break occurs at node |p|, the text // that starts at |pre_break(p)| will precede the break, the text that starts at // |post_break(p)| will follow the break, and text that appears in the next // |replace_count(p)| nodes will be ignored. For example, an ordinary // discretionary hyphen, indicated by `\.[\\-]', yields a |disc_node| with // |pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|, // and |replace_count=0|. All three of the discretionary texts must be // lists that consist entirely of character, kern, box, rule, and ligature nodes. // // If |pre_break(p)=null|, the |ex_hyphen_penalty| will be charged for this // break. Otherwise the |hyphen_penalty| will be charged. The texts will // actually be substituted into the list by the line-breaking algorithm if it // decides to make the break, and the discretionary node will disappear at // that time; thus, the output routine sees only discretionaries that were // not chosen. func (prg *prg) newDisc() (r halfword) { // creates an empty |disc_node| var ( p halfword // the new node ) p = prg.getNode(smallNodeSize) *(*prg.mem[p].hh()).b0() = byte(discNode) *(*prg.mem[p].hh()).b1() = 0 *(*prg.mem[int32(p)+1].hh()).lh() = 0 *(*prg.mem[int32(p)+1].hh()).rh() = 0 r = p return r } // 146. // tangle:pos tex.web:3069:3: // A |whatsit_node| is a wild card reserved for extensions to \TeX. The // |subtype| field in its first word says what `\\[whatsit]' it is, and // implicitly determines the node size (which must be 2 or more) and the // format of the remaining words. When a |whatsit_node| is encountered // in a list, special actions are invoked; knowledgeable people who are // careful not to mess up the rest of \TeX\ are able to make \TeX\ do new // things by adding code at the end of the program. For example, there // might be a `\TeX nicolor' extension to specify different colors of ink, // \xref[extensions to \TeX] // and the whatsit node might contain the desired parameters. // // The present implementation of \TeX\ treats the features associated with // `\.[\\write]' and `\.[\\special]' as if they were extensions, in order to // illustrate how such routines might be coded. We shall defer further // discussion of extensions until the end of this program. // 147. // tangle:pos tex.web:3087:3: // A |math_node|, which occurs only in horizontal lists, appears before and // after mathematical formulas. The |subtype| field is |before| before the // formula and |after| after it. There is a |width| field, which represents // the amount of surrounding space inserted by \.[\\mathsurround]. func (prg *prg) newMath(w scaled, s smallNumber) (r halfword) { var ( p halfword // the new node ) p = prg.getNode(smallNodeSize) *(*prg.mem[p].hh()).b0() = byte(mathNode) *(*prg.mem[p].hh()).b1() = s *prg.mem[int32(p)+widthOffset].int() = w r = p return r } // 148. // tangle:pos tex.web:3102:3: // \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, // |rule_node|, |ins_node|, |mark_node|, |adjust_node|, |ligature_node|, // |disc_node|, |whatsit_node|, and |math_node| are at the low end of the // type codes, by permitting a break at glue in a list if and only if the // |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. // 149. // tangle:pos tex.web:3112:3: // A |glue_node| represents glue in a list. However, it is really only // a pointer to a separate glue specification, since \TeX\ makes use of the // fact that many essentially identical nodes of glue are usually present. // If |p| points to a |glue_node|, |glue_ptr(p)| points to // another packet of words that specify the stretch and shrink components, etc. // // Glue nodes also serve to represent leaders; the |subtype| is used to // distinguish between ordinary glue (which is called |normal|) and the three // kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|). // The |leader_ptr| field points to a rule node or to a box node containing the // leaders; it is set to |null| in ordinary glue nodes. // // Many kinds of glue are computed from \TeX's ``skip'' parameters, and // it is helpful to know which parameter has led to a particular glue node. // Therefore the |subtype| is set to indicate the source of glue, whenever // it originated as a parameter. We will be defining symbolic names for the // parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|, // etc.); it suffices for now to say that the |subtype| of parametric glue // will be the same as the parameter number, plus~one. // // In math formulas there are two more possibilities for the |subtype| in a // glue node: |mu_glue| denotes an \.[\\mskip] (where the units are scaled \.[mu] // instead of scaled \.[pt]); and |cond_math_glue| denotes the `\.[\\nonscript]' // feature that cancels the glue node immediately following if it appears // in a subscript. // 151. // tangle:pos tex.web:3174:3: // Here is a function that returns a pointer to a copy of a glue spec. // The reference count in the copy is |null|, because there is assumed // to be exactly one reference to the new specification. func (prg *prg) newSpec(p halfword) (r halfword) { // duplicates a glue specification var ( q halfword // the new spec ) q = prg.getNode(glueSpecSize) prg.mem[q] = prg.mem[p] *(*prg.mem[q].hh()).rh() = 0 *prg.mem[int32(q)+widthOffset].int() = *prg.mem[int32(p)+widthOffset].int() *prg.mem[int32(q)+2].int() = *prg.mem[int32(p)+2].int() *prg.mem[int32(q)+3].int() = *prg.mem[int32(p)+3].int() r = q return r } // 152. // tangle:pos tex.web:3186:3: // 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]. func (prg *prg) newParamGlue(n smallNumber) (r halfword) { var ( p halfword // the new node q halfword // the glue specification ) p = prg.getNode(smallNodeSize) *(*prg.mem[p].hh()).b0() = byte(glueNode) *(*prg.mem[p].hh()).b1() = byte(int32(n) + 1) *(*prg.mem[int32(p)+1].hh()).rh() = 0 q = *(*prg.eqtb[glueBase+int32(n)-1].hh()).rh() *(*prg.mem[int32(p)+1].hh()).lh() = q *(*prg.mem[q].hh()).rh() = uint16(int32(*(*prg.mem[q].hh()).rh()) + 1) r = p return r } // 153. // tangle:pos tex.web:3201:3: // Glue nodes that are more or less anonymous are created by |new_glue|, // whose argument points to a glue specification. func (prg *prg) newGlue(q halfword) (r halfword) { var ( p halfword // the new node ) p = prg.getNode(smallNodeSize) *(*prg.mem[p].hh()).b0() = byte(glueNode) *(*prg.mem[p].hh()).b1() = byte(normal) *(*prg.mem[int32(p)+1].hh()).rh() = 0 *(*prg.mem[int32(p)+1].hh()).lh() = q *(*prg.mem[q].hh()).rh() = uint16(int32(*(*prg.mem[q].hh()).rh()) + 1) r = p return r } // 154. // tangle:pos tex.web:3211:3: // Still another subroutine is needed: This one is sort of a combination // of |new_param_glue| and |new_glue|. It creates a glue node for one of // the current glue parameters, but it makes a fresh copy of the glue // specification, since that specification will probably be subject to change, // while the parameter will stay put. The global variable |temp_ptr| is // set to the address of the new spec. func (prg *prg) newSkipParam(n smallNumber) (r halfword) { var ( p halfword // the new node ) prg.tempPtr = prg.newSpec(*(*prg.eqtb[glueBase+int32(n)-1].hh()).rh()) p = prg.newGlue(prg.tempPtr) *(*prg.mem[prg.tempPtr].hh()).rh() = 0 *(*prg.mem[p].hh()).b1() = byte(int32(n) + 1) r = p return r } // 155. // tangle:pos tex.web:3225:3: // A |kern_node| has a |width| field to specify a (normally negative) // amount of spacing. This spacing correction appears in horizontal lists // between letters like A and V when the font designer said that it looks // better to move them closer together or further apart. A kern node can // also appear in a vertical list, when its `|width|' denotes additional // 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). // 156. // tangle:pos tex.web:3240:3: // The |new_kern| function creates a kern node having a given width. func (prg *prg) newKern(w scaled) (r halfword) { var ( p halfword // the new node ) p = prg.getNode(smallNodeSize) *(*prg.mem[p].hh()).b0() = byte(kernNode) *(*prg.mem[p].hh()).b1() = byte(normal) *prg.mem[int32(p)+widthOffset].int() = w r = p return r } // 157. // tangle:pos tex.web:3250:3: // A |penalty_node| specifies the penalty associated with line or page // breaking, in its |penalty| field. This field is a fullword integer, but // the full range of integer values is not used: Any penalty |>=10000| is // treated as infinity, and no break will be allowed for such high values. // Similarly, any penalty |<=-10000| is treated as negative infinity, and a // break will be forced. // 158. // tangle:pos tex.web:3262:3: // Anyone who has been reading the last few sections of the program will // be able to guess what comes next. func (prg *prg) newPenalty(m int32) (r halfword) { var ( p halfword // the new node ) p = prg.getNode(smallNodeSize) *(*prg.mem[p].hh()).b0() = byte(penaltyNode) *(*prg.mem[p].hh()).b1() = 0 // the |subtype| is not used *prg.mem[int32(p)+1].int() = m r = p return r } // 159. // tangle:pos tex.web:3272:3: // You might think that we have introduced enough node types by now. Well, // almost, but there is one more: An |unset_node| has nearly the same format // as an |hlist_node| or |vlist_node|; it is used for entries in \.[\\halign] // or \.[\\valign] that are not yet in their final form, since the box // dimensions are their ``natural'' sizes before any glue adjustment has been // made. The |glue_set| word is not present; instead, we have a |glue_stretch| // field, which contains the total stretch of order |glue_order| that is // present in the hlist or vlist being boxed. // Similarly, the |shift_amount| field is replaced by a |glue_shrink| field, // containing the total shrink of order |glue_sign| that is present. // The |subtype| field is called |span_count|; an unset box typically // contains the data for |qo(span_count)+1| columns. // Unset nodes will be changed to box nodes when alignment is completed. // 160. // tangle:pos tex.web:3291:3: // In fact, there are still more types coming. When we get to math formula // processing we will see that a |style_node| has |type=14|; and a number // of larger type codes will also be defined, for use in math mode only. // 161. // tangle:pos tex.web:3295:3: // Warning: If any changes are made to these data structure layouts, such as // changing any of the node sizes or even reordering the words of nodes, // the |copy_node_list| procedure and the memory initialization code // below may have to be changed. Such potentially dangerous parts of the // program are listed in the index under `data structure assumptions'. // \xref[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. // \xref[system dependencies] // 162. \[11] Memory layout // tangle:pos tex.web:3306:24: // Some areas of |mem| are dedicated to fixed usage, since static allocation is // more efficient than dynamic allocation when we can get away with it. For // example, locations |mem_bot| to |mem_bot+3| are always used to store the // specification for glue that is `\.[0pt plus 0pt minus 0pt]'. The // following macro definitions accomplish the static allocation by giving // 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|. // 167. // tangle:pos tex.web:3402:3: // Procedure |check_mem| makes sure that the available space lists of // |mem| are well formed, and it optionally prints out all locations // that are reserved now but were free the last time this procedure was called. // procedure check_mem( print_locs : boolean); // label done1,done2; [loop exits] // var p, q:halfword ; [current locations of interest in |mem|] // clobbered:boolean; [is something amiss?] // 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] // // [ Check single-word |avail| list ] // p:=avail; q:=0 ; clobbered:=false; // while p<>0 do // begin if (p>mem_end)or(p=lo_mem_max)or(p=lo_mem_max)or( mem[ p+ 1].hh.rh lo_mem_max)or ( mem[ mem[ p+ 1].hh.rh + 1].hh.lh <>p) then clobbered:=true; // if clobbered then // begin print_nl(["Double-AVAIL list clobbered at "=]302); // print_int(q); goto done2; // end; // for q:=p to p+ mem[ p].hh.lh -1 do [mark all locations free] // begin if free[q] then // begin print_nl(["Doubly free location at "=]303); // [ \xref[Doubly free location...] ] // print_int(q); goto done2; // end; // free[q]:=true; // end; // q:=p; p:= mem[ p+ 1].hh.rh ; // until p=rover; // done2: // // ; // // [ Check flags of unavailable nodes ] // p:=mem_min; // while p<=lo_mem_max do [node |p| should not be empty] // begin if ( mem[ p].hh.rh = 65535 ) then // begin print_nl(["Bad flag at "=]304); print_int(p); // [ \xref[Bad flag...] ] // end; // while (p<=lo_mem_max) and not free[p] do p:= p+1 ; // while (p<=lo_mem_max) and free[p] do p:= p+1 ; // end // // ; // if print_locs then // [ Print newly busy locations ] // begin print_nl(["New busy locs:"=]305); // 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([" "=]32); print_int(p); // end; // for p:=hi_mem_min to mem_end do // if not free[p] and // ((pwas_mem_end) or was_free[p]) then // begin print_char([" "=]32); print_int(p); // end; // 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; // end; // [ ] // 172. // tangle:pos tex.web:3484:3: // The |search_mem| procedure attempts to answer the question ``Who points // to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem| // that might not be of type |two_halves|. Strictly speaking, this is // \xref[dirty \PASCAL] // undefined in \PASCAL, and it can lead to ``false drops'' (words that seem to // point to |p| purely by coincidence). But for debugging purposes, we want // to rule out the places that do [\sl not\/] point to |p|, so a few false // drops are tolerable. // procedure search_mem( p:halfword ); [look for pointers to |p|] // var q:integer; [current position being searched] // begin for q:=mem_min to lo_mem_max do // begin if mem[ q].hh.rh =p then // begin print_nl(["LINK("=]306); print_int(q); print_char([")"=]41); // end; // if mem[ q].hh.lh =p then // begin print_nl(["INFO("=]307); print_int(q); print_char([")"=]41); // end; // end; // for q:=hi_mem_min to mem_end do // begin if mem[ q].hh.rh =p then // begin print_nl(["LINK("=]306); print_int(q); print_char([")"=]41); // end; // if mem[ q].hh.lh =p then // begin print_nl(["INFO("=]307); print_int(q); print_char([")"=]41); // end; // end; // // [ Search |eqtb| for equivalents equal to |p| ] // for q:=active_base to box_base+255 do // begin if eqtb[ q].hh.rh =p then // begin print_nl(["EQUIV("=]501); print_int(q); print_char([")"=]41); // end; // end // // ; // // [ Search |save_stack| for equivalents that point to |p| ] // if save_ptr>0 then for q:=0 to save_ptr-1 do // begin if save_stack[ q].hh.rh =p then // begin print_nl(["SAVE("=]546); print_int(q); print_char([")"=]41); // end; // 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("=]940); print_int(q); print_char([")"=]41); // end; // end // // ; // end; // [ ] // 174. // tangle:pos tex.web:3538:3: // Boxes, rules, inserts, whatsits, marks, and things in general that are // sort of “complicated” are indicated only by printing `\.[[]]'. func (prg *prg) shortDisplay(p int32) { // prints highlights of list |p| var ( n int32 // for replacement counts ) for p > memMin { if p >= int32(prg.hiMemMin) { if p <= int32(prg.memEnd) { if int32(*(*prg.mem[p].hh()).b0()) != prg.fontInShortDisplay { if int32(*(*prg.mem[p].hh()).b0()) < fontBase || int32(*(*prg.mem[p].hh()).b0()) > fontMax { prg.printChar(asciiCode('*')) } else { // Print the font identifier for |font(p)| prg.printEsc(*prg.hash[fontIdBase+int32(*(*prg.mem[p].hh()).b0())-514].rh()) } prg.printChar(asciiCode(' ')) prg.fontInShortDisplay = int32(*(*prg.mem[p].hh()).b0()) } prg.print(int32(*(*prg.mem[p].hh()).b1()) - minQuarterword) } } else { // Print a short indication of the contents of node |p| switch *(*prg.mem[p].hh()).b0() { case hlistNode, vlistNode, insNode, whatsitNode, markNode, adjustNode, unsetNode: prg.print( /* "[]" */ 308) case ruleNode: prg.printChar(asciiCode('|')) case glueNode: if int32(*(*prg.mem[p+1].hh()).lh()) != memBot { prg.printChar(asciiCode(' ')) } case mathNode: prg.printChar(asciiCode('$')) case ligatureNode: prg.shortDisplay(int32(*(*prg.mem[p+1].hh()).rh())) case discNode: prg.shortDisplay(int32(*(*prg.mem[p+1].hh()).lh())) prg.shortDisplay(int32(*(*prg.mem[p+1].hh()).rh())) n = int32(*(*prg.mem[p].hh()).b1()) for n > 0 { if int32(*(*prg.mem[p].hh()).rh()) != 0 { p = int32(*(*prg.mem[p].hh()).rh()) } n = n - 1 } default: } } p = int32(*(*prg.mem[p].hh()).rh()) } } // 176. // tangle:pos tex.web:3580:3: // The |show_node_list| routine requires some auxiliary subroutines: one to // print a font-and-character combination, one to print a token list without // its reference count, and one to print a rule dimension. func (prg *prg) printFontAndChar(p int32) { if p > int32(prg.memEnd) { prg.printEsc(strNumber( /* "CLOBBERED." */ 309)) } else { if int32(*(*prg.mem[p].hh()).b0()) < fontBase || int32(*(*prg.mem[p].hh()).b0()) > fontMax { prg.printChar(asciiCode('*')) } else { // Print the font identifier for |font(p)| prg.printEsc(*prg.hash[fontIdBase+int32(*(*prg.mem[p].hh()).b0())-514].rh()) } prg.printChar(asciiCode(' ')) prg.print(int32(*(*prg.mem[p].hh()).b1()) - minQuarterword) } } func (prg *prg) printMark(p int32) { prg.printChar(asciiCode('{')) if p < int32(prg.hiMemMin) || p > int32(prg.memEnd) { prg.printEsc(strNumber( /* "CLOBBERED." */ 309)) } else { prg.showTokenList(int32(*(*prg.mem[p].hh()).rh()), 0, maxPrintLine-10) } prg.printChar(asciiCode('}')) } func (prg *prg) printRuleDimen(d scaled) { if d == -010000000000 { prg.printChar(asciiCode('*')) } else { prg.printScaled(d) } // \xref[*\relax] } // 177. // tangle:pos tex.web:3605:3: // Then there is a subroutine that prints glue stretch and shrink, possibly // followed by the name of finite units: func (prg *prg) printGlue(d scaled, order int32, s strNumber) { prg.printScaled(d) if order < normal || order > filll { prg.print( /* "foul" */ 310) } else if order > normal { prg.print( /* "fil" */ 311) for order > fil { prg.printChar(asciiCode('l')) order = order - 1 } } else if int32(s) != 0 { prg.print(int32(s)) } } // 178. // tangle:pos tex.web:3621:3: // The next subroutine prints a whole glue specification. func (prg *prg) printSpec(p int32, s strNumber) { if p < memMin || p >= int32(prg.loMemMax) { prg.printChar(asciiCode('*')) } else { prg.printScaled(*prg.mem[p+widthOffset].int()) if int32(s) != 0 { prg.print(int32(s)) } if *prg.mem[p+2].int() != 0 { prg.print( /* " plus " */ 312) prg.printGlue(*prg.mem[p+2].int(), int32(*(*prg.mem[p].hh()).b0()), s) } if *prg.mem[p+3].int() != 0 { prg.print( /* " minus " */ 313) prg.printGlue(*prg.mem[p+3].int(), int32(*(*prg.mem[p].hh()).b1()), s) } } } // 179. // tangle:pos tex.web:3638:3: // We also need to declare some procedures that appear later in this // documentation. // Declare procedures needed for displaying the elements of mlists func (prg *prg) printFamAndChar(p halfword) { prg.printEsc(strNumber( /* "fam" */ 464)) prg.printInt(int32(*(*prg.mem[p].hh()).b0())) prg.printChar(asciiCode(' ')) prg.print(int32(*(*prg.mem[p].hh()).b1()) - minQuarterword) } func (prg *prg) printDelimiter(p halfword) { // prints a delimiter as 24-bit hex value var ( a int32 // accumulator ) a = int32((*prg.mem[p].qqqq()).b0)*256 + int32((*prg.mem[p].qqqq()).b1) - minQuarterword a = a*010000 + int32((*prg.mem[p].qqqq()).b2)*256 + int32((*prg.mem[p].qqqq()).b3) - minQuarterword if a < 0 { prg.printInt(a) } else { prg.printHex(a) } } // \2 // |show_node_list(info(temp_ptr))| func (prg *prg) printSubsidiaryData(p halfword, c asciiCode) { if int32(prg.poolPtr)-int32(prg.strStart[prg.strPtr]) >= prg.depthThreshold { if int32(*(*prg.mem[p].hh()).rh()) != empty { prg.print( /* " []" */ 314) } } else { { prg.strPool[prg.poolPtr] = c prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } // include |c| in the recursion history prg.tempPtr = p // prepare for |show_info| if recursion is needed switch *(*prg.mem[p].hh()).rh() { case mathChar: prg.printLn() prg.printCurrentString() prg.printFamAndChar(p) case subBox: prg.showInfo() // recursive call case subMlist: if int32(*(*prg.mem[p].hh()).lh()) == 0 { prg.printLn() prg.printCurrentString() prg.print( /* "[]" */ 860) } else { prg.showInfo() } // recursive call default: // |empty| } prg.poolPtr = uint16(int32(prg.poolPtr) - 1) // remove |c| from the recursion history } } func (prg *prg) printStyle(c int32) { switch c / 2 { case 0: prg.printEsc(strNumber( /* "displaystyle" */ 861)) // |display_style=0| case 1: prg.printEsc(strNumber( /* "textstyle" */ 862)) // |text_style=2| case 2: prg.printEsc(strNumber( /* "scriptstyle" */ 863)) // |script_style=4| case 3: prg.printEsc(strNumber( /* "scriptscriptstyle" */ 864)) // |script_script_style=6| default: prg.print( /* "Unknown style!" */ 865) } } // Declare the procedure called |print_skip_param| func (prg *prg) printSkipParam(n int32) { switch n { case lineSkipCode: prg.printEsc(strNumber( /* "lineskip" */ 376)) case baselineSkipCode: prg.printEsc(strNumber( /* "baselineskip" */ 377)) case parSkipCode: prg.printEsc(strNumber( /* "parskip" */ 378)) case aboveDisplaySkipCode: prg.printEsc(strNumber( /* "abovedisplayskip" */ 379)) case belowDisplaySkipCode: prg.printEsc(strNumber( /* "belowdisplayskip" */ 380)) case aboveDisplayShortSkipCode: prg.printEsc(strNumber( /* "abovedisplayshortskip" */ 381)) case belowDisplayShortSkipCode: prg.printEsc(strNumber( /* "belowdisplayshortskip" */ 382)) case leftSkipCode: prg.printEsc(strNumber( /* "leftskip" */ 383)) case rightSkipCode: prg.printEsc(strNumber( /* "rightskip" */ 384)) case topSkipCode: prg.printEsc(strNumber( /* "topskip" */ 385)) case splitTopSkipCode: prg.printEsc(strNumber( /* "splittopskip" */ 386)) case tabSkipCode: prg.printEsc(strNumber( /* "tabskip" */ 387)) case spaceSkipCode: prg.printEsc(strNumber( /* "spaceskip" */ 388)) case xspaceSkipCode: prg.printEsc(strNumber( /* "xspaceskip" */ 389)) case parFillSkipCode: prg.printEsc(strNumber( /* "parfillskip" */ 390)) case thinMuSkipCode: prg.printEsc(strNumber( /* "thinmuskip" */ 391)) case medMuSkipCode: prg.printEsc(strNumber( /* "medmuskip" */ 392)) case thickMuSkipCode: prg.printEsc(strNumber( /* "thickmuskip" */ 393)) default: prg.print( /* "[unknown glue parameter!]" */ 394) } } // 180. // tangle:pos tex.web:3644:3: // Since boxes can be inside of boxes, |show_node_list| is inherently recursive, // \xref[recursion] // up to a given maximum number of levels. The history of nesting is indicated // by the current string, which will be printed at the beginning of each line; // the length of this string, namely |cur_length|, is the depth of nesting. // // Recursive calls on |show_node_list| therefore use the following pattern: // 182. // tangle:pos tex.web:3667:3: // Now we are ready for |show_node_list| itself. This procedure has been // written to be “extra robust” in the sense that it should not crash or get // into a loop even if the data structures have been messed up by bugs in // the rest of the program. You can safely call its parent routine // |show_box(p)| for arbitrary values of |p| when you are debugging \TeX. // However, in the presence of bad data, the procedure may // \xref[dirty \PASCAL]\xref[debugging] // fetch a |memory_word| whose variant is different from the way it was stored; // for example, it might try to read |mem[p].hh| when |mem[p]| // contains a scaled integer, if |p| is a pointer that has been // clobbered or chosen at random. func (prg *prg) showNodeList(p int32) { var ( n int32 // the number of items already printed at this level g float64 // a glue ratio, as a floating point number ) if int32(prg.poolPtr)-int32(prg.strStart[prg.strPtr]) > prg.depthThreshold { if p > 0 { prg.print( /* " []" */ 314) } // indicate that there's been some truncation // indicate that there's been some truncation goto exit } n = 0 for p > memMin { prg.printLn() prg.printCurrentString() // display the nesting history if p > int32(prg.memEnd) { prg.print( /* "Bad link, display aborted." */ 315) goto exit // \xref[Bad link...] } n = n + 1 if n > prg.breadthMax { prg.print( /* "etc." */ 316) goto exit // \xref[etc] } // Display node |p| if p >= int32(prg.hiMemMin) { prg.printFontAndChar(p) } else { switch *(*prg.mem[p].hh()).b0() { case hlistNode, vlistNode, unsetNode: // Display box |p| if int32(*(*prg.mem[p].hh()).b0()) == hlistNode { prg.printEsc(strNumber('h')) } else if int32(*(*prg.mem[p].hh()).b0()) == vlistNode { prg.printEsc(strNumber('v')) } else { prg.printEsc(strNumber( /* "unset" */ 318)) } prg.print( /* "box(" */ 319) prg.printScaled(*prg.mem[p+heightOffset].int()) prg.printChar(asciiCode('+')) prg.printScaled(*prg.mem[p+depthOffset].int()) prg.print( /* ")x" */ 320) prg.printScaled(*prg.mem[p+widthOffset].int()) if int32(*(*prg.mem[p].hh()).b0()) == unsetNode { if int32(*(*prg.mem[p].hh()).b1()) != minQuarterword { prg.print( /* " (" */ 286) prg.printInt(int32(*(*prg.mem[p].hh()).b1()) - minQuarterword + 1) prg.print( /* " columns)" */ 322) } if *prg.mem[p+glueOffset].int() != 0 { prg.print( /* ", stretch " */ 323) prg.printGlue(*prg.mem[p+glueOffset].int(), int32(*(*prg.mem[p+listOffset].hh()).b1()), strNumber(0)) } if *prg.mem[p+4].int() != 0 { prg.print( /* ", shrink " */ 324) prg.printGlue(*prg.mem[p+4].int(), int32(*(*prg.mem[p+listOffset].hh()).b0()), strNumber(0)) } } else { g = float64(*prg.mem[p+glueOffset].gr()) if g != 0.0 && int32(*(*prg.mem[p+listOffset].hh()).b0()) != normal { prg.print( /* ", glue set " */ 325) if int32(*(*prg.mem[p+listOffset].hh()).b0()) == shrinking { prg.print( /* "- " */ 326) } if abs(*prg.mem[p+glueOffset].int()) < 04000000 { prg.print( /* "?.?" */ 327) } else if fabs(g) > 20000.0 { if g > 0.0 { prg.printChar(asciiCode('>')) } else { prg.print( /* "< -" */ 328) } prg.printGlue(20000*0200000, int32(*(*prg.mem[p+listOffset].hh()).b1()), strNumber(0)) } else { prg.printGlue(round(float64(0200000)*g), int32(*(*prg.mem[p+listOffset].hh()).b1()), strNumber(0)) } // \xref[real multiplication] } if *prg.mem[p+4].int() != 0 { prg.print( /* ", shifted " */ 321) prg.printScaled(*prg.mem[p+4].int()) } } { { prg.strPool[prg.poolPtr] = '.' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } prg.showNodeList(int32(*(*prg.mem[p+listOffset].hh()).rh())) prg.poolPtr = uint16(int32(prg.poolPtr) - 1) } // recursive call case ruleNode: // Display rule |p| prg.printEsc(strNumber( /* "rule(" */ 329)) prg.printRuleDimen(*prg.mem[p+heightOffset].int()) prg.printChar(asciiCode('+')) prg.printRuleDimen(*prg.mem[p+depthOffset].int()) prg.print( /* ")x" */ 320) prg.printRuleDimen(*prg.mem[p+widthOffset].int()) case insNode: // Display insertion |p| prg.printEsc(strNumber( /* "insert" */ 330)) prg.printInt(int32(*(*prg.mem[p].hh()).b1()) - minQuarterword) prg.print( /* ", natural size " */ 331) prg.printScaled(*prg.mem[p+heightOffset].int()) prg.print( /* "; split(" */ 332) prg.printSpec(int32(*(*prg.mem[p+4].hh()).rh()), strNumber(0)) prg.printChar(asciiCode(',')) prg.printScaled(*prg.mem[p+depthOffset].int()) prg.print( /* "); float cost " */ 333) prg.printInt(*prg.mem[p+1].int()) { { prg.strPool[prg.poolPtr] = '.' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } prg.showNodeList(int32(*(*prg.mem[p+4].hh()).lh())) prg.poolPtr = uint16(int32(prg.poolPtr) - 1) } // recursive call case whatsitNode: // Display the whatsit node |p| switch *(*prg.mem[p].hh()).b1() { case openNode: prg.printWriteWhatsit(strNumber( /* "openout" */ 1285), halfword(p)) prg.printChar(asciiCode('=')) prg.printFileName(int32(*(*prg.mem[p+1].hh()).rh()), int32(*(*prg.mem[p+2].hh()).lh()), int32(*(*prg.mem[p+2].hh()).rh())) case writeNode: prg.printWriteWhatsit(strNumber( /* "write" */ 594), halfword(p)) prg.printMark(int32(*(*prg.mem[p+1].hh()).rh())) case closeNode: prg.printWriteWhatsit(strNumber( /* "closeout" */ 1286), halfword(p)) case specialNode: prg.printEsc(strNumber( /* "special" */ 1287)) prg.printMark(int32(*(*prg.mem[p+1].hh()).rh())) case languageNode: prg.printEsc(strNumber( /* "setlanguage" */ 1289)) prg.printInt(int32(*(*prg.mem[p+1].hh()).rh())) prg.print( /* " (hyphenmin " */ 1292) prg.printInt(int32(*(*prg.mem[p+1].hh()).b0())) prg.printChar(asciiCode(',')) prg.printInt(int32(*(*prg.mem[p+1].hh()).b1())) prg.printChar(asciiCode(')')) default: prg.print( /* "whatsit?" */ 1293) } case glueNode: // Display glue |p| if int32(*(*prg.mem[p].hh()).b1()) >= aLeaders { prg.printEsc(strNumber( /* "" */ 338)) if int32(*(*prg.mem[p].hh()).b1()) == cLeaders { prg.printChar(asciiCode('c')) } else if int32(*(*prg.mem[p].hh()).b1()) == xLeaders { prg.printChar(asciiCode('x')) } prg.print( /* "leaders " */ 339) prg.printSpec(int32(*(*prg.mem[p+1].hh()).lh()), strNumber(0)) { { prg.strPool[prg.poolPtr] = '.' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } prg.showNodeList(int32(*(*prg.mem[p+1].hh()).rh())) prg.poolPtr = uint16(int32(prg.poolPtr) - 1) } // recursive call } else { prg.printEsc(strNumber( /* "glue" */ 334)) if int32(*(*prg.mem[p].hh()).b1()) != normal { prg.printChar(asciiCode('(')) if int32(*(*prg.mem[p].hh()).b1()) < condMathGlue { prg.printSkipParam(int32(*(*prg.mem[p].hh()).b1()) - 1) } else if int32(*(*prg.mem[p].hh()).b1()) == condMathGlue { prg.printEsc(strNumber( /* "nonscript" */ 335)) } else { prg.printEsc(strNumber( /* "mskip" */ 336)) } prg.printChar(asciiCode(')')) } if int32(*(*prg.mem[p].hh()).b1()) != condMathGlue { prg.printChar(asciiCode(' ')) if int32(*(*prg.mem[p].hh()).b1()) < condMathGlue { prg.printSpec(int32(*(*prg.mem[p+1].hh()).lh()), strNumber(0)) } else { prg.printSpec(int32(*(*prg.mem[p+1].hh()).lh()), strNumber( /* "mu" */ 337)) } } } case kernNode: // Display kern |p| if int32(*(*prg.mem[p].hh()).b1()) != muGlue { prg.printEsc(strNumber( /* "kern" */ 340)) if int32(*(*prg.mem[p].hh()).b1()) != normal { prg.printChar(asciiCode(' ')) } prg.printScaled(*prg.mem[p+widthOffset].int()) if int32(*(*prg.mem[p].hh()).b1()) == accKern { prg.print( /* " (for accent)" */ 341) } // \xref[for accent] } else { prg.printEsc(strNumber( /* "mkern" */ 342)) prg.printScaled(*prg.mem[p+widthOffset].int()) prg.print( /* "mu" */ 337) } case mathNode: // Display math node |p| prg.printEsc(strNumber( /* "math" */ 343)) if int32(*(*prg.mem[p].hh()).b1()) == before { prg.print( /* "on" */ 344) } else { prg.print( /* "off" */ 345) } if *prg.mem[p+widthOffset].int() != 0 { prg.print( /* ", surrounded " */ 346) prg.printScaled(*prg.mem[p+widthOffset].int()) } case ligatureNode: // Display ligature |p| prg.printFontAndChar(p + 1) prg.print( /* " (ligature " */ 347) if int32(*(*prg.mem[p].hh()).b1()) > 1 { prg.printChar(asciiCode('|')) } prg.fontInShortDisplay = int32(*(*prg.mem[p+1].hh()).b0()) prg.shortDisplay(int32(*(*prg.mem[p+1].hh()).rh())) if *(*prg.mem[p].hh()).b1()&1 != 0 { prg.printChar(asciiCode('|')) } prg.printChar(asciiCode(')')) case penaltyNode: // Display penalty |p| prg.printEsc(strNumber( /* "penalty " */ 348)) prg.printInt(*prg.mem[p+1].int()) case discNode: // Display discretionary |p| prg.printEsc(strNumber( /* "discretionary" */ 349)) if int32(*(*prg.mem[p].hh()).b1()) > 0 { prg.print( /* " replacing " */ 350) prg.printInt(int32(*(*prg.mem[p].hh()).b1())) } { { prg.strPool[prg.poolPtr] = '.' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } prg.showNodeList(int32(*(*prg.mem[p+1].hh()).lh())) prg.poolPtr = uint16(int32(prg.poolPtr) - 1) } // recursive call { prg.strPool[prg.poolPtr] = '|' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } prg.showNodeList(int32(*(*prg.mem[p+1].hh()).rh())) prg.poolPtr = uint16(int32(prg.poolPtr) - 1) // recursive call case markNode: // Display mark |p| prg.printEsc(strNumber( /* "mark" */ 351)) prg.printMark(*prg.mem[p+1].int()) case adjustNode: // Display adjustment |p| prg.printEsc(strNumber( /* "vadjust" */ 352)) { { prg.strPool[prg.poolPtr] = '.' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } prg.showNodeList(*prg.mem[p+1].int()) prg.poolPtr = uint16(int32(prg.poolPtr) - 1) } // recursive call // \4 // Cases of |show_node_list| that arise in mlists only case styleNode: prg.printStyle(int32(*(*prg.mem[p].hh()).b1())) case choiceNode: // Display choice node |p| prg.printEsc(strNumber( /* "mathchoice" */ 525)) { prg.strPool[prg.poolPtr] = 'D' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } prg.showNodeList(int32(*(*prg.mem[p+1].hh()).lh())) prg.poolPtr = uint16(int32(prg.poolPtr) - 1) { prg.strPool[prg.poolPtr] = 'T' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } prg.showNodeList(int32(*(*prg.mem[p+1].hh()).rh())) prg.poolPtr = uint16(int32(prg.poolPtr) - 1) { prg.strPool[prg.poolPtr] = 'S' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } prg.showNodeList(int32(*(*prg.mem[p+2].hh()).lh())) prg.poolPtr = uint16(int32(prg.poolPtr) - 1) { prg.strPool[prg.poolPtr] = 's' prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } prg.showNodeList(int32(*(*prg.mem[p+2].hh()).rh())) prg.poolPtr = uint16(int32(prg.poolPtr) - 1) case ordNoad, opNoad, binNoad, relNoad, openNoad, closeNoad, punctNoad, innerNoad, radicalNoad, overNoad, underNoad, vcenterNoad, accentNoad, leftNoad, rightNoad: // Display normal noad |p| switch *(*prg.mem[p].hh()).b0() { case ordNoad: prg.printEsc(strNumber( /* "mathord" */ 866)) case opNoad: prg.printEsc(strNumber( /* "mathop" */ 867)) case binNoad: prg.printEsc(strNumber( /* "mathbin" */ 868)) case relNoad: prg.printEsc(strNumber( /* "mathrel" */ 869)) case openNoad: prg.printEsc(strNumber( /* "mathopen" */ 870)) case closeNoad: prg.printEsc(strNumber( /* "mathclose" */ 871)) case punctNoad: prg.printEsc(strNumber( /* "mathpunct" */ 872)) case innerNoad: prg.printEsc(strNumber( /* "mathinner" */ 873)) case overNoad: prg.printEsc(strNumber( /* "overline" */ 874)) case underNoad: prg.printEsc(strNumber( /* "underline" */ 875)) case vcenterNoad: prg.printEsc(strNumber( /* "vcenter" */ 539)) case radicalNoad: prg.printEsc(strNumber( /* "radical" */ 533)) prg.printDelimiter(halfword(p + 4)) case accentNoad: prg.printEsc(strNumber( /* "accent" */ 508)) prg.printFamAndChar(halfword(p + 4)) case leftNoad: prg.printEsc(strNumber( /* "left" */ 876)) prg.printDelimiter(halfword(p + 1)) case rightNoad: prg.printEsc(strNumber( /* "right" */ 877)) prg.printDelimiter(halfword(p + 1)) } if int32(*(*prg.mem[p].hh()).b1()) != normal { if int32(*(*prg.mem[p].hh()).b1()) == limits { prg.printEsc(strNumber( /* "limits" */ 878)) } else { prg.printEsc(strNumber( /* "nolimits" */ 879)) } } if int32(*(*prg.mem[p].hh()).b0()) < leftNoad { prg.printSubsidiaryData(halfword(p+1), asciiCode('.')) } prg.printSubsidiaryData(halfword(p+2), asciiCode('^')) prg.printSubsidiaryData(halfword(p+3), asciiCode('_')) case fractionNoad: // Display fraction noad |p| prg.printEsc(strNumber( /* "fraction, thickness " */ 880)) if *prg.mem[p+widthOffset].int() == 010000000000 { prg.print( /* "= default" */ 881) } else { prg.printScaled(*prg.mem[p+widthOffset].int()) } if int32((*prg.mem[p+4].qqqq()).b0) != 0 || int32((*prg.mem[p+4].qqqq()).b1) != minQuarterword || int32((*prg.mem[p+4].qqqq()).b2) != 0 || int32((*prg.mem[p+4].qqqq()).b3) != minQuarterword { prg.print( /* ", left-delimiter " */ 882) prg.printDelimiter(halfword(p + 4)) } if int32((*prg.mem[p+5].qqqq()).b0) != 0 || int32((*prg.mem[p+5].qqqq()).b1) != minQuarterword || int32((*prg.mem[p+5].qqqq()).b2) != 0 || int32((*prg.mem[p+5].qqqq()).b3) != minQuarterword { prg.print( /* ", right-delimiter " */ 883) prg.printDelimiter(halfword(p + 5)) } prg.printSubsidiaryData(halfword(p+2), asciiCode('\\')) prg.printSubsidiaryData(halfword(p+3), asciiCode('/')) default: prg.print( /* "Unknown node type!" */ 317) } } p = int32(*(*prg.mem[p].hh()).rh()) } exit: } // 198. // tangle:pos tex.web:3872:3: // The recursive machinery is started by calling |show_box|. // \xref[recursion] func (prg *prg) showBox(p halfword) { prg.depthThreshold = *prg.eqtb[intBase+showBoxDepthCode-1].int() prg.breadthMax = *prg.eqtb[intBase+showBoxBreadthCode-1].int() if prg.breadthMax <= 0 { prg.breadthMax = 5 } if int32(prg.poolPtr)+prg.depthThreshold >= poolSize { prg.depthThreshold = poolSize - int32(prg.poolPtr) - 1 } // now there's enough room for prefix string prg.showNodeList(int32(p)) // the show starts at |p| prg.printLn() } // 199. \[13] Destroying boxes // tangle:pos tex.web:3886:27: // When we are done with a node list, we are obliged to return it to free // storage, including all of its sublists. The recursive procedure // |flush_node_list| does this for us. // 200. // tangle:pos tex.web:3891:3: // First, however, we shall consider two non-recursive procedures that do // simpler tasks. The first of these, |delete_token_ref|, is called when // a pointer to a token list's reference count is being removed. This means // that the token list should disappear if the reference count was |null|, // otherwise the count should be decreased by one. // \xref[reference counts] func (prg *prg) deleteTokenRef(p halfword) { if int32(*(*prg.mem[p].hh()).lh()) == 0 { prg.flushList(p) } else { *(*prg.mem[p].hh()).lh() = uint16(int32(*(*prg.mem[p].hh()).lh()) - 1) } } // 201. // tangle:pos tex.web:3906:3: // Similarly, |delete_glue_ref| is called when a pointer to a glue // specification is being withdrawn. // \xref[reference counts] func (prg *prg) deleteGlueRef(p halfword) { if int32(*(*prg.mem[p].hh()).rh()) == 0 { prg.freeNode(p, halfword(glueSpecSize)) } else { *(*prg.mem[p].hh()).rh() = uint16(int32(*(*prg.mem[p].hh()).rh()) - 1) } } // 202. // tangle:pos tex.web:3917:3: // Now we are ready to delete any node list, recursively. // 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. // \xref[recursion] func (prg *prg) flushNodeList(p halfword) { // go here when node |p| has been freed var ( q halfword // successor to node |p| ) for int32(p) != 0 { // \xref[inner loop] q = *(*prg.mem[p].hh()).rh() if int32(p) >= int32(prg.hiMemMin) { *(*prg.mem[p].hh()).rh() = prg.avail prg.avail = p /* dyn_used:= dyn_used-1 ; [ ] */ } else { switch *(*prg.mem[p].hh()).b0() { case hlistNode, vlistNode, unsetNode: prg.flushNodeList(*(*prg.mem[int32(p)+listOffset].hh()).rh()) prg.freeNode(p, halfword(boxNodeSize)) goto done case ruleNode: prg.freeNode(p, halfword(ruleNodeSize)) goto done case insNode: prg.flushNodeList(*(*prg.mem[int32(p)+4].hh()).lh()) prg.deleteGlueRef(*(*prg.mem[int32(p)+4].hh()).rh()) prg.freeNode(p, halfword(insNodeSize)) goto done case whatsitNode: // Wipe out the whatsit node |p| and |goto done| switch *(*prg.mem[p].hh()).b1() { case openNode: prg.freeNode(p, halfword(openNodeSize)) case writeNode, specialNode: prg.deleteTokenRef(*(*prg.mem[int32(p)+1].hh()).rh()) prg.freeNode(p, halfword(writeNodeSize)) goto done case closeNode, languageNode: prg.freeNode(p, halfword(smallNodeSize)) default: prg.confusion(strNumber( /* "ext3" */ 1295)) // \xref[this can't happen ext3][\quad ext3] } goto done case glueNode: { if int32(*(*prg.mem[*(*prg.mem[int32(p)+1].hh()).lh()].hh()).rh()) == 0 { prg.freeNode(*(*prg.mem[int32(p)+1].hh()).lh(), halfword(glueSpecSize)) } else { *(*prg.mem[*(*prg.mem[int32(p)+1].hh()).lh()].hh()).rh() = uint16(int32(*(*prg.mem[*(*prg.mem[int32(p)+1].hh()).lh()].hh()).rh()) - 1) } } if int32(*(*prg.mem[int32(p)+1].hh()).rh()) != 0 { prg.flushNodeList(*(*prg.mem[int32(p)+1].hh()).rh()) } case kernNode, mathNode, penaltyNode: case ligatureNode: prg.flushNodeList(*(*prg.mem[int32(p)+1].hh()).rh()) case markNode: prg.deleteTokenRef(halfword(*prg.mem[int32(p)+1].int())) case discNode: prg.flushNodeList(*(*prg.mem[int32(p)+1].hh()).lh()) prg.flushNodeList(*(*prg.mem[int32(p)+1].hh()).rh()) case adjustNode: prg.flushNodeList(halfword(*prg.mem[int32(p)+1].int())) // \4 // Cases of |flush_node_list| that arise in mlists only case styleNode: prg.freeNode(p, halfword(styleNodeSize)) goto done case choiceNode: prg.flushNodeList(*(*prg.mem[int32(p)+1].hh()).lh()) prg.flushNodeList(*(*prg.mem[int32(p)+1].hh()).rh()) prg.flushNodeList(*(*prg.mem[int32(p)+2].hh()).lh()) prg.flushNodeList(*(*prg.mem[int32(p)+2].hh()).rh()) prg.freeNode(p, halfword(styleNodeSize)) goto done case ordNoad, opNoad, binNoad, relNoad, openNoad, closeNoad, punctNoad, innerNoad, radicalNoad, overNoad, underNoad, vcenterNoad, accentNoad: // if int32(*(*prg.mem[int32(p)+1].hh()).rh()) >= subBox { prg.flushNodeList(*(*prg.mem[int32(p)+1].hh()).lh()) } if int32(*(*prg.mem[int32(p)+2].hh()).rh()) >= subBox { prg.flushNodeList(*(*prg.mem[int32(p)+2].hh()).lh()) } if int32(*(*prg.mem[int32(p)+3].hh()).rh()) >= subBox { prg.flushNodeList(*(*prg.mem[int32(p)+3].hh()).lh()) } if int32(*(*prg.mem[p].hh()).b0()) == radicalNoad { prg.freeNode(p, halfword(radicalNoadSize)) } else if int32(*(*prg.mem[p].hh()).b0()) == accentNoad { prg.freeNode(p, halfword(accentNoadSize)) } else { prg.freeNode(p, halfword(noadSize)) } goto done case leftNoad, rightNoad: prg.freeNode(p, halfword(noadSize)) goto done case fractionNoad: prg.flushNodeList(*(*prg.mem[int32(p)+2].hh()).lh()) prg.flushNodeList(*(*prg.mem[int32(p)+3].hh()).lh()) prg.freeNode(p, halfword(fractionNoadSize)) goto done default: prg.confusion(strNumber( /* "flushing" */ 353)) // \xref[this can't happen flushing][\quad flushing] } prg.freeNode(p, halfword(smallNodeSize)) done: } p = q } } // 203. \[14] Copying boxes // tangle:pos tex.web:3960:24: // 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 // halfword, since each pointer to a node is in a different memory address, // and the total number of memory addresses fits in a halfword. // \xref[recursion] // \xref[reference counts] // // (Well, there actually are also references from outside |mem|; if the // |save_stack| is made arbitrarily large, it would theoretically be possible // to break \TeX\ by overflowing a reference count. But who would want to do that?) // 204. // tangle:pos tex.web:3978:3: // The copying procedure copies words en masse without bothering // to look at their individual fields. If the node format changes---for // example, if the size is altered, or if some link field is moved to another // relative position---then this code may need to be changed too. // \xref[data structure assumptions] func (prg *prg) copyNodeList(p halfword) (r halfword) { // makes a duplicate of the // node list that starts at |p| and returns a pointer to the new list var ( h halfword // temporary head of copied list q halfword // previous position in new list r1 halfword // current node being fabricated for new list words/* 0..5 */ byte // number of words remaining to be copied ) h = prg.getAvail() q = h for int32(p) != 0 { words = 1 // this setting occurs in more branches than any other if int32(p) >= int32(prg.hiMemMin) { r1 = prg.getAvail() } else { // Case statement to copy different types and set |words| to the number of initial words not yet copied switch *(*prg.mem[p].hh()).b0() { case hlistNode, vlistNode, unsetNode: r1 = prg.getNode(boxNodeSize) prg.mem[int32(r1)+6] = prg.mem[int32(p)+6] prg.mem[int32(r1)+5] = prg.mem[int32(p)+5] // copy the last two words *(*prg.mem[int32(r1)+listOffset].hh()).rh() = prg.copyNodeList(*(*prg.mem[int32(p)+listOffset].hh()).rh()) // this affects |mem[r+5]| words = 5 case ruleNode: r1 = prg.getNode(ruleNodeSize) words = byte(ruleNodeSize) case insNode: r1 = prg.getNode(insNodeSize) prg.mem[int32(r1)+4] = prg.mem[int32(p)+4] *(*prg.mem[*(*prg.mem[int32(p)+4].hh()).rh()].hh()).rh() = uint16(int32(*(*prg.mem[*(*prg.mem[int32(p)+4].hh()).rh()].hh()).rh()) + 1) *(*prg.mem[int32(r1)+4].hh()).lh() = prg.copyNodeList(*(*prg.mem[int32(p)+4].hh()).lh()) // this affects |mem[r+4]| words = byte(insNodeSize - 1) case whatsitNode: // Make a partial copy of the whatsit node |p| and make |r| point to it; set |words| to the number of initial words not yet copied switch *(*prg.mem[p].hh()).b1() { case openNode: r1 = prg.getNode(openNodeSize) words = byte(openNodeSize) case writeNode, specialNode: r1 = prg.getNode(writeNodeSize) *(*prg.mem[*(*prg.mem[int32(p)+1].hh()).rh()].hh()).lh() = uint16(int32(*(*prg.mem[*(*prg.mem[int32(p)+1].hh()).rh()].hh()).lh()) + 1) words = byte(writeNodeSize) case closeNode, languageNode: r1 = prg.getNode(smallNodeSize) words = byte(smallNodeSize) default: prg.confusion(strNumber( /* "ext2" */ 1294)) // \xref[this can't happen ext2][\quad ext2] } case glueNode: r1 = prg.getNode(smallNodeSize) *(*prg.mem[*(*prg.mem[int32(p)+1].hh()).lh()].hh()).rh() = uint16(int32(*(*prg.mem[*(*prg.mem[int32(p)+1].hh()).lh()].hh()).rh()) + 1) *(*prg.mem[int32(r1)+1].hh()).lh() = *(*prg.mem[int32(p)+1].hh()).lh() *(*prg.mem[int32(r1)+1].hh()).rh() = prg.copyNodeList(*(*prg.mem[int32(p)+1].hh()).rh()) case kernNode, mathNode, penaltyNode: r1 = prg.getNode(smallNodeSize) words = byte(smallNodeSize) case ligatureNode: r1 = prg.getNode(smallNodeSize) prg.mem[int32(r1)+1] = prg.mem[int32(p)+1] // copy |font| and |character| *(*prg.mem[int32(r1)+1].hh()).rh() = prg.copyNodeList(*(*prg.mem[int32(p)+1].hh()).rh()) case discNode: r1 = prg.getNode(smallNodeSize) *(*prg.mem[int32(r1)+1].hh()).lh() = prg.copyNodeList(*(*prg.mem[int32(p)+1].hh()).lh()) *(*prg.mem[int32(r1)+1].hh()).rh() = prg.copyNodeList(*(*prg.mem[int32(p)+1].hh()).rh()) case markNode: r1 = prg.getNode(smallNodeSize) *(*prg.mem[*prg.mem[int32(p)+1].int()].hh()).lh() = uint16(int32(*(*prg.mem[*prg.mem[int32(p)+1].int()].hh()).lh()) + 1) words = byte(smallNodeSize) case adjustNode: r1 = prg.getNode(smallNodeSize) *prg.mem[int32(r1)+1].int() = int32(prg.copyNodeList(halfword(*prg.mem[int32(p)+1].int()))) // |words=1=small_node_size-1| default: prg.confusion(strNumber( /* "copying" */ 354)) // \xref[this can't happen copying][\quad copying] } } for int32(words) > 0 { words = byte(int32(words) - 1) prg.mem[int32(r1)+int32(words)] = prg.mem[int32(p)+int32(words)] } *(*prg.mem[q].hh()).rh() = r1 q = r1 p = *(*prg.mem[p].hh()).rh() } *(*prg.mem[q].hh()).rh() = 0 q = *(*prg.mem[h].hh()).rh() { *(*prg.mem[h].hh()).rh() = prg.avail prg.avail = h /* dyn_used:= dyn_used-1 ; [ ] */ } r = q return r } // 207. \[15] The command codes // tangle:pos tex.web:4048:28: // Before we can go any further, we need to define symbolic names for the internal // code numbers that represent the various commands obeyed by \TeX. These codes // are somewhat arbitrary, but not completely so. For example, the command // codes for character types are fixed by the language, since a user says, // 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 // been made adjacent so that |case| statements in the program need not consider // cases that are widely spaced, or so that |case| statements can be replaced // by |if| statements. // // At any rate, here is the list, for future reference. First come the // ``catcode'' commands, several of which share their numeric codes with // ordinary commands when the catcode cannot emerge from \TeX's scanning routine. // 208. // tangle:pos tex.web:4090:3: // 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]'. // 209. // tangle:pos tex.web:4154:3: // The next codes are special; they all relate to mode-independent // assignment of values to \TeX's internal registers or tables. // Codes that are |max_internal| or less represent internal quantities // that might be expanded by `\.[\\the]'. // 210. // tangle:pos tex.web:4194:3: // The remaining command codes are extra special, since they cannot get through // \TeX's scanner to the main control routine. They have been given values higher // than |max_command| so that their special nature is easily discernible. // The ``expandable'' commands come first. // 211. \[16] The semantic nest // tangle:pos tex.web:4220:28: // \TeX\ is typically in the midst of building many lists at once. For example, // when a math formula is being processed, \TeX\ is in math mode and // working on an mlist; this formula has temporarily interrupted \TeX\ from // being in horizontal mode and building the hlist of a paragraph; and this // paragraph has temporarily interrupted \TeX\ from being in vertical mode // and building the vlist for the next page of a document. Similarly, when a // \.[\\vbox] occurs inside of an \.[\\hbox], \TeX\ is temporarily // interrupted from working in restricted horizontal mode, and it enters // internal vertical mode. The “semantic nest” is a stack that // keeps track of what lists and modes are currently suspended. // // At each level of processing we are in one of six modes: // // \yskip\hang|vmode| stands for vertical mode (the page builder); // // \hang|hmode| stands for horizontal mode (the paragraph builder); // // \hang|mmode| stands for displayed formula mode; // // \hang|-vmode| stands for internal vertical mode (e.g., in a \.[\\vbox]); // // \hang|-hmode| stands for restricted horizontal mode (e.g., in an \.[\\hbox]); // // \hang|-mmode| stands for math formula mode (not displayed). // // \yskip\noindent The mode is temporarily set to zero while processing \.[\\write] // texts. // // Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that // \TeX's “big semantic switch” can select the appropriate thing to // do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current // mode and |cur_cmd| is the current command code. func (prg *prg) printMode(m int32) { if m > 0 { switch m / (maxCommand + 1) { case 0: prg.print( /* "vertical" */ 355) case 1: prg.print( /* "horizontal" */ 356) case 2: prg.print( /* "display math" */ 357) } } else if m == 0 { prg.print( /* "no" */ 358) } else { switch -m / (maxCommand + 1) { case 0: prg.print( /* "internal vertical" */ 359) case 1: prg.print( /* "restricted horizontal" */ 360) case 2: prg.print( /* "math" */ 343) } } prg.print( /* " mode" */ 361) } // 214. // tangle:pos tex.web:4348:3: // Here is a common way to make the current list grow: // 216. // tangle:pos tex.web:4368:3: // When \TeX's work on one level is interrupted, the state is saved by // calling |push_nest|. This routine changes |head| and |tail| so that // a new (empty) list is begun; it does not change |mode| or |aux|. func (prg *prg) pushNest() { if int32(prg.nestPtr) > int32(prg.maxNestStack) { prg.maxNestStack = prg.nestPtr if int32(prg.nestPtr) == nestSize { prg.overflow(strNumber( /* "semantic nest size" */ 362), nestSize) } // \xref[TeX capacity exceeded semantic nest size][\quad semantic nest size] } prg.nest[prg.nestPtr] = prg.curList // stack the record prg.nestPtr = byte(int32(prg.nestPtr) + 1) prg.curList.headField = prg.getAvail() prg.curList.tailField = prg.curList.headField prg.curList.pgField = 0 prg.curList.mlField = prg.line } // 217. // tangle:pos tex.web:4382:3: // Conversely, when \TeX\ is finished on the current level, the former // state is restored by calling |pop_nest|. This routine will never be // called at the lowest semantic level, nor will it be called unless |head| // is a node that should be returned to free memory. func (prg *prg) popNest() { { *(*prg.mem[prg.curList.headField].hh()).rh() = prg.avail prg.avail = prg.curList.headField /* dyn_used:= dyn_used-1 ; [ ] */ } prg.nestPtr = byte(int32(prg.nestPtr) - 1) prg.curList = prg.nest[prg.nestPtr] } // \2 func (prg *prg) showActivities() { var ( p/* 0..nestSize */ byte // index into |nest| m/* -mmode..mmode */ int16 // mode a memoryWord // auxiliary q, r1 halfword // for showing the current page t int32 // ditto ) prg.nest[prg.nestPtr] = prg.curList // put the top level into the array prg.printNl(strNumber( /* "" */ 338)) prg.printLn() for ii := int32(prg.nestPtr); ii >= 0; ii-- { p = byte(ii) _ = p m = prg.nest[p].modeField a = prg.nest[p].auxField prg.printNl(strNumber( /* "### " */ 363)) prg.printMode(int32(m)) prg.print( /* " entered at line " */ 364) prg.printInt(abs(prg.nest[p].mlField)) if int32(m) == hmode { if prg.nest[p].pgField != 040600000 { prg.print( /* " (language" */ 365) prg.printInt(prg.nest[p].pgField % 0200000) prg.print( /* ":hyphenmin" */ 366) prg.printInt(prg.nest[p].pgField / 020000000) prg.printChar(asciiCode(',')) prg.printInt(prg.nest[p].pgField / 0200000 % 0100) prg.printChar(asciiCode(')')) } } if prg.nest[p].mlField < 0 { prg.print( /* " (\\output routine)" */ 367) } if int32(p) == 0 { if 30000-2 != int32(prg.pageTail) { prg.printNl(strNumber( /* "### current page:" */ 980)) if prg.outputActive { prg.print( /* " (held over for next output)" */ 981) } // \xref[held over for next output] prg.showBox(*(*prg.mem[30000-2].hh()).rh()) if int32(prg.pageContents) > empty { prg.printNl(strNumber( /* "total height " */ 982)) prg.printTotals() // \xref[total_height][\.[total height]] prg.printNl(strNumber( /* " goal height " */ 983)) prg.printScaled(prg.pageSoFar[0]) // \xref[goal height] r1 = *(*prg.mem[30000].hh()).rh() for int32(r1) != 30000 { prg.printLn() prg.printEsc(strNumber( /* "insert" */ 330)) t = int32(*(*prg.mem[r1].hh()).b1()) - minQuarterword prg.printInt(t) prg.print( /* " adds " */ 984) if *prg.eqtb[countBase+t-1].int() == 1000 { t = *prg.mem[int32(r1)+heightOffset].int() } else { t = prg.xOverN(*prg.mem[int32(r1)+heightOffset].int(), 1000) * *prg.eqtb[countBase+t-1].int() } prg.printScaled(t) if int32(*(*prg.mem[r1].hh()).b0()) == splitUp { q = uint16(30000 - 2) t = 0 for { q = *(*prg.mem[q].hh()).rh() if int32(*(*prg.mem[q].hh()).b0()) == insNode && int32(*(*prg.mem[q].hh()).b1()) == int32(*(*prg.mem[r1].hh()).b1()) { t = t + 1 } if int32(q) == int32(*(*prg.mem[int32(r1)+1].hh()).lh()) { break } } prg.print( /* ", #" */ 985) prg.printInt(t) prg.print( /* " might split" */ 986) } r1 = *(*prg.mem[r1].hh()).rh() } } } if int32(*(*prg.mem[30000-1].hh()).rh()) != 0 { prg.printNl(strNumber( /* "### recent contributions:" */ 368)) } } prg.showBox(*(*prg.mem[prg.nest[p].headField].hh()).rh()) // Show the auxiliary field, |a| switch abs(int32(m)) / (maxCommand + 1) { case 0: prg.printNl(strNumber( /* "prevdepth " */ 369)) if *a.int() <= -65536000 { prg.print( /* "ignored" */ 370) } else { prg.printScaled(*a.int()) } if prg.nest[p].pgField != 0 { prg.print( /* ", prevgraf " */ 371) prg.printInt(prg.nest[p].pgField) prg.print( /* " line" */ 372) if prg.nest[p].pgField != 1 { prg.printChar(asciiCode('s')) } } case 1: prg.printNl(strNumber( /* "spacefactor " */ 373)) prg.printInt(int32(*(*a.hh()).lh())) if int32(m) > 0 { if int32(*(*a.hh()).rh()) > 0 { prg.print( /* ", current language " */ 374) prg.printInt(int32(*(*a.hh()).rh())) } } case 2: if *a.int() != 0 { prg.print( /* "this will begin denominator of:" */ 375) prg.showBox(halfword(*a.int())) } } } } // 220. \[17] The table of equivalents // tangle:pos tex.web:4444:35: // Now that we have studied the data structures for \TeX's semantic routines, // 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 // what the user has written. // // The biggest and most important such table is called |eqtb|. It holds the // current ``equivalents'' of things; i.e., it explains what things mean // or what their current values are, for all quantities that are subject to // the nesting structure provided by \TeX's grouping mechanism. There are six // parts to |eqtb|: // // \yskip\hangg 1) |eqtb[active_base..(hash_base-1)]| holds the current // equivalents of single-character control sequences. // // \yskip\hangg 2) |eqtb[hash_base..(glue_base-1)]| holds the current // equivalents of multiletter control sequences. // // \yskip\hangg 3) |eqtb[glue_base..(local_base-1)]| holds the current // equivalents of glue parameters like the current baselineskip. // // \yskip\hangg 4) |eqtb[local_base..(int_base-1)]| holds the current // equivalents of local halfword quantities like the current box registers, // the current ``catcodes,'' the current font, and a pointer to the current // paragraph shape. // // \yskip\hangg 5) |eqtb[int_base..(dimen_base-1)]| holds the current // equivalents of fullword integer parameters like the current hyphenation // penalty. // // \yskip\hangg 6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents // of fullword dimension parameters like the current hsize or amount of // hanging indentation. // // \yskip\noindent Note that, for example, the current amount of // baselineskip glue is determined by the setting of a particular location // in region~3 of |eqtb|, while the current meaning of the control sequence // `\.[\\baselineskip]' (which might have been changed by \.[\\def] or // \.[\\let]) appears in region~2. // 221. // tangle:pos tex.web:4485:3: // Each entry in |eqtb| is a |memory_word|. Most of these words are of type // |two_halves|, and subdivided into three fields: // // \yskip\hangg 1) The |eq_level| (a quarterword) is the level of grouping at // which this equivalent was defined. If the level is |level_zero|, the // equivalent has never been defined; |level_one| refers to the outer level // (outside of all groups), and this level is also used for global // definitions that never go away. Higher levels are for equivalents that // will disappear at the end of their group. \xref[global definitions] // // \yskip\hangg 2) The |eq_type| (another quarterword) specifies what kind of // entry this is. There are many types, since each \TeX\ primitive like // \.[\\hbox], \.[\\def], etc., has its own special code. The list of // command codes above includes all possible settings of the |eq_type| field. // // \yskip\hangg 3) The |equiv| (a halfword) is the current equivalent value. // This may be a font number, a pointer into |mem|, or a variety of other // things. // 237. // tangle:pos tex.web:5043:3: // We can print the symbolic name of an integer parameter as follows. func (prg *prg) printParam(n int32) { switch n { case pretoleranceCode: prg.printEsc(strNumber( /* "pretolerance" */ 420)) case toleranceCode: prg.printEsc(strNumber( /* "tolerance" */ 421)) case linePenaltyCode: prg.printEsc(strNumber( /* "linepenalty" */ 422)) case hyphenPenaltyCode: prg.printEsc(strNumber( /* "hyphenpenalty" */ 423)) case exHyphenPenaltyCode: prg.printEsc(strNumber( /* "exhyphenpenalty" */ 424)) case clubPenaltyCode: prg.printEsc(strNumber( /* "clubpenalty" */ 425)) case widowPenaltyCode: prg.printEsc(strNumber( /* "widowpenalty" */ 426)) case displayWidowPenaltyCode: prg.printEsc(strNumber( /* "displaywidowpenalty" */ 427)) case brokenPenaltyCode: prg.printEsc(strNumber( /* "brokenpenalty" */ 428)) case binOpPenaltyCode: prg.printEsc(strNumber( /* "binoppenalty" */ 429)) case relPenaltyCode: prg.printEsc(strNumber( /* "relpenalty" */ 430)) case preDisplayPenaltyCode: prg.printEsc(strNumber( /* "predisplaypenalty" */ 431)) case postDisplayPenaltyCode: prg.printEsc(strNumber( /* "postdisplaypenalty" */ 432)) case interLinePenaltyCode: prg.printEsc(strNumber( /* "interlinepenalty" */ 433)) case doubleHyphenDemeritsCode: prg.printEsc(strNumber( /* "doublehyphendemerits" */ 434)) case finalHyphenDemeritsCode: prg.printEsc(strNumber( /* "finalhyphendemerits" */ 435)) case adjDemeritsCode: prg.printEsc(strNumber( /* "adjdemerits" */ 436)) case magCode: prg.printEsc(strNumber( /* "mag" */ 437)) case delimiterFactorCode: prg.printEsc(strNumber( /* "delimiterfactor" */ 438)) case loosenessCode: prg.printEsc(strNumber( /* "looseness" */ 439)) case timeCode: prg.printEsc(strNumber( /* "time" */ 440)) case dayCode: prg.printEsc(strNumber( /* "day" */ 441)) case monthCode: prg.printEsc(strNumber( /* "month" */ 442)) case yearCode: prg.printEsc(strNumber( /* "year" */ 443)) case showBoxBreadthCode: prg.printEsc(strNumber( /* "showboxbreadth" */ 444)) case showBoxDepthCode: prg.printEsc(strNumber( /* "showboxdepth" */ 445)) case hbadnessCode: prg.printEsc(strNumber( /* "hbadness" */ 446)) case vbadnessCode: prg.printEsc(strNumber( /* "vbadness" */ 447)) case pausingCode: prg.printEsc(strNumber( /* "pausing" */ 448)) case tracingOnlineCode: prg.printEsc(strNumber( /* "tracingonline" */ 449)) case tracingMacrosCode: prg.printEsc(strNumber( /* "tracingmacros" */ 450)) case tracingStatsCode: prg.printEsc(strNumber( /* "tracingstats" */ 451)) case tracingParagraphsCode: prg.printEsc(strNumber( /* "tracingparagraphs" */ 452)) case tracingPagesCode: prg.printEsc(strNumber( /* "tracingpages" */ 453)) case tracingOutputCode: prg.printEsc(strNumber( /* "tracingoutput" */ 454)) case tracingLostCharsCode: prg.printEsc(strNumber( /* "tracinglostchars" */ 455)) case tracingCommandsCode: prg.printEsc(strNumber( /* "tracingcommands" */ 456)) case tracingRestoresCode: prg.printEsc(strNumber( /* "tracingrestores" */ 457)) case ucHyphCode: prg.printEsc(strNumber( /* "uchyph" */ 458)) case outputPenaltyCode: prg.printEsc(strNumber( /* "outputpenalty" */ 459)) case maxDeadCyclesCode: prg.printEsc(strNumber( /* "maxdeadcycles" */ 460)) case hangAfterCode: prg.printEsc(strNumber( /* "hangafter" */ 461)) case floatingPenaltyCode: prg.printEsc(strNumber( /* "floatingpenalty" */ 462)) case globalDefsCode: prg.printEsc(strNumber( /* "globaldefs" */ 463)) case curFamCode: prg.printEsc(strNumber( /* "fam" */ 464)) case escapeCharCode: prg.printEsc(strNumber( /* "escapechar" */ 465)) case defaultHyphenCharCode: prg.printEsc(strNumber( /* "defaulthyphenchar" */ 466)) case defaultSkewCharCode: prg.printEsc(strNumber( /* "defaultskewchar" */ 467)) case endLineCharCode: prg.printEsc(strNumber( /* "endlinechar" */ 468)) case newLineCharCode: prg.printEsc(strNumber( /* "newlinechar" */ 469)) case languageCode: prg.printEsc(strNumber( /* "language" */ 470)) case leftHyphenMinCode: prg.printEsc(strNumber( /* "lefthyphenmin" */ 471)) case rightHyphenMinCode: prg.printEsc(strNumber( /* "righthyphenmin" */ 472)) case holdingInsertsCode: prg.printEsc(strNumber( /* "holdinginserts" */ 473)) case errorContextLinesCode: prg.printEsc(strNumber( /* "errorcontextlines" */ 474)) default: prg.print( /* "[unknown integer parameter!]" */ 475) } } // 241. // tangle:pos tex.web:5240:3: // The following procedure, which is called just before \TeX\ initializes its // input and output, establishes the initial values of the date and time. // \xref[system dependencies] // Since standard \PASCAL\ cannot provide such information, something special // is needed. The program here simply assumes that suitable values appear in // the global variables \\[sys\_time], \\[sys\_day], \\[sys\_month], and // \\[sys\_year] (which are initialized to noon on 4 July 1776, // in case the implementor is careless). func (prg *prg) fixDateAndTime() { prg.sysTime = 12 * 60 prg.sysDay = 4 prg.sysMonth = 7 prg.sysYear = 1776 // self-evident truths *prg.eqtb[intBase+timeCode-1].int() = prg.sysTime // minutes since midnight *prg.eqtb[intBase+dayCode-1].int() = prg.sysDay // day of the month *prg.eqtb[intBase+monthCode-1].int() = prg.sysMonth // month of the year *prg.eqtb[intBase+yearCode-1].int() = prg.sysYear // Anno Domini } // 245. // tangle:pos tex.web:5272:3: // \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: func (prg *prg) beginDiagnostic() { prg.oldSetting = prg.selector if *prg.eqtb[intBase+tracingOnlineCode-1].int() <= 0 && int32(prg.selector) == termAndLog { prg.selector = byte(int32(prg.selector) - 1) if int32(prg.history) == spotless { prg.history = byte(warningIssued) } } } func (prg *prg) endDiagnostic(blankLine bool) { prg.printNl(strNumber( /* "" */ 338)) if blankLine { prg.printLn() } prg.selector = prg.oldSetting } // 247. // tangle:pos tex.web:5299:3: // The final region of |eqtb| contains the dimension parameters defined // here, and the 256 \.[\\dimen] registers. func (prg *prg) printLengthParam(n int32) { switch n { case parIndentCode: prg.printEsc(strNumber( /* "parindent" */ 478)) case mathSurroundCode: prg.printEsc(strNumber( /* "mathsurround" */ 479)) case lineSkipLimitCode: prg.printEsc(strNumber( /* "lineskiplimit" */ 480)) case hsizeCode: prg.printEsc(strNumber( /* "hsize" */ 481)) case vsizeCode: prg.printEsc(strNumber( /* "vsize" */ 482)) case maxDepthCode: prg.printEsc(strNumber( /* "maxdepth" */ 483)) case splitMaxDepthCode: prg.printEsc(strNumber( /* "splitmaxdepth" */ 484)) case boxMaxDepthCode: prg.printEsc(strNumber( /* "boxmaxdepth" */ 485)) case hfuzzCode: prg.printEsc(strNumber( /* "hfuzz" */ 486)) case vfuzzCode: prg.printEsc(strNumber( /* "vfuzz" */ 487)) case delimiterShortfallCode: prg.printEsc(strNumber( /* "delimitershortfall" */ 488)) case nullDelimiterSpaceCode: prg.printEsc(strNumber( /* "nulldelimiterspace" */ 489)) case scriptSpaceCode: prg.printEsc(strNumber( /* "scriptspace" */ 490)) case preDisplaySizeCode: prg.printEsc(strNumber( /* "predisplaysize" */ 491)) case displayWidthCode: prg.printEsc(strNumber( /* "displaywidth" */ 492)) case displayIndentCode: prg.printEsc(strNumber( /* "displayindent" */ 493)) case overfullRuleCode: prg.printEsc(strNumber( /* "overfullrule" */ 494)) case hangIndentCode: prg.printEsc(strNumber( /* "hangindent" */ 495)) case hOffsetCode: prg.printEsc(strNumber( /* "hoffset" */ 496)) case vOffsetCode: prg.printEsc(strNumber( /* "voffset" */ 497)) case emergencyStretchCode: prg.printEsc(strNumber( /* "emergencystretch" */ 498)) default: prg.print( /* "[unknown dimen parameter!]" */ 499) } } // 252. // tangle:pos tex.web:5441:3: // Here is a procedure that displays the contents of |eqtb[n]| // symbolically. // \4 // Declare the procedure called |print_cmd_chr| func (prg *prg) printCmdChr(cmd quarterword, chrCode halfword) { switch cmd { case leftBrace: prg.print( /* "begin-group character " */ 557) prg.print(int32(chrCode)) case rightBrace: prg.print( /* "end-group character " */ 558) prg.print(int32(chrCode)) case mathShift: prg.print( /* "math shift character " */ 559) prg.print(int32(chrCode)) case macParam: prg.print( /* "macro parameter character " */ 560) prg.print(int32(chrCode)) case supMark: prg.print( /* "superscript character " */ 561) prg.print(int32(chrCode)) case subMark: prg.print( /* "subscript character " */ 562) prg.print(int32(chrCode)) case endv: prg.print( /* "end of alignment template" */ 563) case spacer: prg.print( /* "blank space " */ 564) prg.print(int32(chrCode)) case letter: prg.print( /* "the letter " */ 565) prg.print(int32(chrCode)) case otherChar: prg.print( /* "the character " */ 566) prg.print(int32(chrCode)) // \4 // Cases of |print_cmd_chr| for symbolic printing of primitives case assignGlue, assignMuGlue: if int32(chrCode) < skipBase { prg.printSkipParam(int32(chrCode) - glueBase) } else if int32(chrCode) < muSkipBase { prg.printEsc(strNumber( /* "skip" */ 395)) prg.printInt(int32(chrCode) - skipBase) } else { prg.printEsc(strNumber( /* "muskip" */ 396)) prg.printInt(int32(chrCode) - muSkipBase) } case assignToks: if int32(chrCode) >= toksBase { prg.printEsc(strNumber( /* "toks" */ 407)) prg.printInt(int32(chrCode) - toksBase) } else { switch chrCode { case outputRoutineLoc: prg.printEsc(strNumber( /* "output" */ 398)) case everyParLoc: prg.printEsc(strNumber( /* "everypar" */ 399)) case everyMathLoc: prg.printEsc(strNumber( /* "everymath" */ 400)) case everyDisplayLoc: prg.printEsc(strNumber( /* "everydisplay" */ 401)) case everyHboxLoc: prg.printEsc(strNumber( /* "everyhbox" */ 402)) case everyVboxLoc: prg.printEsc(strNumber( /* "everyvbox" */ 403)) case everyJobLoc: prg.printEsc(strNumber( /* "everyjob" */ 404)) case everyCrLoc: prg.printEsc(strNumber( /* "everycr" */ 405)) default: prg.printEsc(strNumber( /* "errhelp" */ 406)) } } case assignInt: if int32(chrCode) < countBase { prg.printParam(int32(chrCode) - intBase) } else { prg.printEsc(strNumber( /* "count" */ 476)) prg.printInt(int32(chrCode) - countBase) } case assignDimen: if int32(chrCode) < scaledBase { prg.printLengthParam(int32(chrCode) - dimenBase) } else { prg.printEsc(strNumber( /* "dimen" */ 500)) prg.printInt(int32(chrCode) - scaledBase) } case accent: prg.printEsc(strNumber( /* "accent" */ 508)) case advance: prg.printEsc(strNumber( /* "advance" */ 509)) case afterAssignment: prg.printEsc(strNumber( /* "afterassignment" */ 510)) case afterGroup: prg.printEsc(strNumber( /* "aftergroup" */ 511)) case assignFontDimen: prg.printEsc(strNumber( /* "fontdimen" */ 519)) case beginGroup: prg.printEsc(strNumber( /* "begingroup" */ 512)) case breakPenalty: prg.printEsc(strNumber( /* "penalty" */ 531)) case charNum: prg.printEsc(strNumber( /* "char" */ 513)) case csName: prg.printEsc(strNumber( /* "csname" */ 504)) case defFont: prg.printEsc(strNumber( /* "font" */ 518)) case delimNum: prg.printEsc(strNumber( /* "delimiter" */ 514)) case divide: prg.printEsc(strNumber( /* "divide" */ 515)) case endCsName: prg.printEsc(strNumber( /* "endcsname" */ 505)) case endGroup: prg.printEsc(strNumber( /* "endgroup" */ 516)) case exSpace: prg.printEsc(strNumber(' ')) case expandAfter: prg.printEsc(strNumber( /* "expandafter" */ 517)) case halign: prg.printEsc(strNumber( /* "halign" */ 520)) case hrule: prg.printEsc(strNumber( /* "hrule" */ 521)) case ignoreSpaces: prg.printEsc(strNumber( /* "ignorespaces" */ 522)) case insert: prg.printEsc(strNumber( /* "insert" */ 330)) case italCorr: prg.printEsc(strNumber('/')) case mark: prg.printEsc(strNumber( /* "mark" */ 351)) case mathAccent: prg.printEsc(strNumber( /* "mathaccent" */ 523)) case mathCharNum: prg.printEsc(strNumber( /* "mathchar" */ 524)) case mathChoice: prg.printEsc(strNumber( /* "mathchoice" */ 525)) case multiply: prg.printEsc(strNumber( /* "multiply" */ 526)) case noAlign: prg.printEsc(strNumber( /* "noalign" */ 527)) case noBoundary: prg.printEsc(strNumber( /* "noboundary" */ 528)) case noExpand: prg.printEsc(strNumber( /* "noexpand" */ 529)) case nonScript: prg.printEsc(strNumber( /* "nonscript" */ 335)) case omit: prg.printEsc(strNumber( /* "omit" */ 530)) case radical: prg.printEsc(strNumber( /* "radical" */ 533)) case readToCs: prg.printEsc(strNumber( /* "read" */ 534)) case relax: prg.printEsc(strNumber( /* "relax" */ 535)) case setBox: prg.printEsc(strNumber( /* "setbox" */ 536)) case setPrevGraf: prg.printEsc(strNumber( /* "prevgraf" */ 532)) case setShape: prg.printEsc(strNumber( /* "parshape" */ 408)) case the: prg.printEsc(strNumber( /* "the" */ 537)) case toksRegister: prg.printEsc(strNumber( /* "toks" */ 407)) case vadjust: prg.printEsc(strNumber( /* "vadjust" */ 352)) case valign: prg.printEsc(strNumber( /* "valign" */ 538)) case vcenter: prg.printEsc(strNumber( /* "vcenter" */ 539)) case vrule: prg.printEsc(strNumber( /* "vrule" */ 540)) case parEnd: prg.printEsc(strNumber( /* "par" */ 597)) case input: if int32(chrCode) == 0 { prg.printEsc(strNumber( /* "input" */ 629)) } else { prg.printEsc(strNumber( /* "endinput" */ 630)) } case topBotMark: switch chrCode { case firstMarkCode: prg.printEsc(strNumber( /* "firstmark" */ 632)) case botMarkCode: prg.printEsc(strNumber( /* "botmark" */ 633)) case splitFirstMarkCode: prg.printEsc(strNumber( /* "splitfirstmark" */ 634)) case splitBotMarkCode: prg.printEsc(strNumber( /* "splitbotmark" */ 635)) default: prg.printEsc(strNumber( /* "topmark" */ 631)) } case register: if int32(chrCode) == intVal { prg.printEsc(strNumber( /* "count" */ 476)) } else if int32(chrCode) == dimenVal { prg.printEsc(strNumber( /* "dimen" */ 500)) } else if int32(chrCode) == glueVal { prg.printEsc(strNumber( /* "skip" */ 395)) } else { prg.printEsc(strNumber( /* "muskip" */ 396)) } case setAux: if int32(chrCode) == vmode { prg.printEsc(strNumber( /* "prevdepth" */ 669)) } else { prg.printEsc(strNumber( /* "spacefactor" */ 668)) } case setPageInt: if int32(chrCode) == 0 { prg.printEsc(strNumber( /* "deadcycles" */ 670)) } else { prg.printEsc(strNumber( /* "insertpenalties" */ 671)) } case setBoxDimen: if int32(chrCode) == widthOffset { prg.printEsc(strNumber( /* "wd" */ 672)) } else if int32(chrCode) == heightOffset { prg.printEsc(strNumber( /* "ht" */ 673)) } else { prg.printEsc(strNumber( /* "dp" */ 674)) } case lastItem: switch chrCode { case intVal: prg.printEsc(strNumber( /* "lastpenalty" */ 675)) case dimenVal: prg.printEsc(strNumber( /* "lastkern" */ 676)) case glueVal: prg.printEsc(strNumber( /* "lastskip" */ 677)) case inputLineNoCode: prg.printEsc(strNumber( /* "inputlineno" */ 678)) default: prg.printEsc(strNumber( /* "badness" */ 679)) } case convert: switch chrCode { case numberCode: prg.printEsc(strNumber( /* "number" */ 735)) case romanNumeralCode: prg.printEsc(strNumber( /* "romannumeral" */ 736)) case stringCode: prg.printEsc(strNumber( /* "string" */ 737)) case meaningCode: prg.printEsc(strNumber( /* "meaning" */ 738)) case fontNameCode: prg.printEsc(strNumber( /* "fontname" */ 739)) default: prg.printEsc(strNumber( /* "jobname" */ 740)) } case ifTest: switch chrCode { case ifCatCode: prg.printEsc(strNumber( /* "ifcat" */ 758)) case ifIntCode: prg.printEsc(strNumber( /* "ifnum" */ 759)) case ifDimCode: prg.printEsc(strNumber( /* "ifdim" */ 760)) case ifOddCode: prg.printEsc(strNumber( /* "ifodd" */ 761)) case ifVmodeCode: prg.printEsc(strNumber( /* "ifvmode" */ 762)) case ifHmodeCode: prg.printEsc(strNumber( /* "ifhmode" */ 763)) case ifMmodeCode: prg.printEsc(strNumber( /* "ifmmode" */ 764)) case ifInnerCode: prg.printEsc(strNumber( /* "ifinner" */ 765)) case ifVoidCode: prg.printEsc(strNumber( /* "ifvoid" */ 766)) case ifHboxCode: prg.printEsc(strNumber( /* "ifhbox" */ 767)) case ifVboxCode: prg.printEsc(strNumber( /* "ifvbox" */ 768)) case ifxCode: prg.printEsc(strNumber( /* "ifx" */ 769)) case ifEofCode: prg.printEsc(strNumber( /* "ifeof" */ 770)) case ifTrueCode: prg.printEsc(strNumber( /* "iftrue" */ 771)) case ifFalseCode: prg.printEsc(strNumber( /* "iffalse" */ 772)) case ifCaseCode: prg.printEsc(strNumber( /* "ifcase" */ 773)) default: prg.printEsc(strNumber( /* "if" */ 757)) } case fiOrElse: if int32(chrCode) == fiCode { prg.printEsc(strNumber( /* "fi" */ 774)) } else if int32(chrCode) == orCode { prg.printEsc(strNumber( /* "or" */ 775)) } else { prg.printEsc(strNumber( /* "else" */ 776)) } case tabMark: if int32(chrCode) == spanCode { prg.printEsc(strNumber( /* "span" */ 898)) } else { prg.print( /* "alignment tab character " */ 902) prg.print(int32(chrCode)) } case carRet: if int32(chrCode) == crCode { prg.printEsc(strNumber( /* "cr" */ 899)) } else { prg.printEsc(strNumber( /* "crcr" */ 900)) } case setPageDimen: switch chrCode { case 0: prg.printEsc(strNumber( /* "pagegoal" */ 970)) case 1: prg.printEsc(strNumber( /* "pagetotal" */ 971)) case 2: prg.printEsc(strNumber( /* "pagestretch" */ 972)) case 3: prg.printEsc(strNumber( /* "pagefilstretch" */ 973)) case 4: prg.printEsc(strNumber( /* "pagefillstretch" */ 974)) case 5: prg.printEsc(strNumber( /* "pagefilllstretch" */ 975)) case 6: prg.printEsc(strNumber( /* "pageshrink" */ 976)) default: prg.printEsc(strNumber( /* "pagedepth" */ 977)) } case stop: if int32(chrCode) == 1 { prg.printEsc(strNumber( /* "dump" */ 1026)) } else { prg.printEsc(strNumber( /* "end" */ 1025)) } case hskip: switch chrCode { case skipCode: prg.printEsc(strNumber( /* "hskip" */ 1027)) case filCode: prg.printEsc(strNumber( /* "hfil" */ 1028)) case fillCode: prg.printEsc(strNumber( /* "hfill" */ 1029)) case ssCode: prg.printEsc(strNumber( /* "hss" */ 1030)) default: prg.printEsc(strNumber( /* "hfilneg" */ 1031)) } case vskip: switch chrCode { case skipCode: prg.printEsc(strNumber( /* "vskip" */ 1032)) case filCode: prg.printEsc(strNumber( /* "vfil" */ 1033)) case fillCode: prg.printEsc(strNumber( /* "vfill" */ 1034)) case ssCode: prg.printEsc(strNumber( /* "vss" */ 1035)) default: prg.printEsc(strNumber( /* "vfilneg" */ 1036)) } case mskip: prg.printEsc(strNumber( /* "mskip" */ 336)) case kern: prg.printEsc(strNumber( /* "kern" */ 340)) case mkern: prg.printEsc(strNumber( /* "mkern" */ 342)) case hmove: if int32(chrCode) == 1 { prg.printEsc(strNumber( /* "moveleft" */ 1054)) } else { prg.printEsc(strNumber( /* "moveright" */ 1055)) } case vmove: if int32(chrCode) == 1 { prg.printEsc(strNumber( /* "raise" */ 1056)) } else { prg.printEsc(strNumber( /* "lower" */ 1057)) } case makeBox: switch chrCode { case boxCode: prg.printEsc(strNumber( /* "box" */ 409)) case copyCode: prg.printEsc(strNumber( /* "copy" */ 1058)) case lastBoxCode: prg.printEsc(strNumber( /* "lastbox" */ 1059)) case vsplitCode: prg.printEsc(strNumber( /* "vsplit" */ 965)) case vtopCode: prg.printEsc(strNumber( /* "vtop" */ 1060)) case vtopCode + 1: prg.printEsc(strNumber( /* "vbox" */ 967)) default: prg.printEsc(strNumber( /* "hbox" */ 1061)) } case leaderShip: if int32(chrCode) == aLeaders { prg.printEsc(strNumber( /* "leaders" */ 1063)) } else if int32(chrCode) == cLeaders { prg.printEsc(strNumber( /* "cleaders" */ 1064)) } else if int32(chrCode) == xLeaders { prg.printEsc(strNumber( /* "xleaders" */ 1065)) } else { prg.printEsc(strNumber( /* "shipout" */ 1062)) } case startPar: if int32(chrCode) == 0 { prg.printEsc(strNumber( /* "noindent" */ 1081)) } else { prg.printEsc(strNumber( /* "indent" */ 1080)) } case removeItem: if int32(chrCode) == glueNode { prg.printEsc(strNumber( /* "unskip" */ 1092)) } else if int32(chrCode) == kernNode { prg.printEsc(strNumber( /* "unkern" */ 1091)) } else { prg.printEsc(strNumber( /* "unpenalty" */ 1090)) } case unHbox: if int32(chrCode) == copyCode { prg.printEsc(strNumber( /* "unhcopy" */ 1094)) } else { prg.printEsc(strNumber( /* "unhbox" */ 1093)) } case unVbox: if int32(chrCode) == copyCode { prg.printEsc(strNumber( /* "unvcopy" */ 1096)) } else { prg.printEsc(strNumber( /* "unvbox" */ 1095)) } case discretionary: if int32(chrCode) == 1 { prg.printEsc(strNumber('-')) } else { prg.printEsc(strNumber( /* "discretionary" */ 349)) } case eqNo: if int32(chrCode) == 1 { prg.printEsc(strNumber( /* "leqno" */ 1128)) } else { prg.printEsc(strNumber( /* "eqno" */ 1127)) } case mathComp: switch chrCode { case ordNoad: prg.printEsc(strNumber( /* "mathord" */ 866)) case opNoad: prg.printEsc(strNumber( /* "mathop" */ 867)) case binNoad: prg.printEsc(strNumber( /* "mathbin" */ 868)) case relNoad: prg.printEsc(strNumber( /* "mathrel" */ 869)) case openNoad: prg.printEsc(strNumber( /* "mathopen" */ 870)) case closeNoad: prg.printEsc(strNumber( /* "mathclose" */ 871)) case punctNoad: prg.printEsc(strNumber( /* "mathpunct" */ 872)) case innerNoad: prg.printEsc(strNumber( /* "mathinner" */ 873)) case underNoad: prg.printEsc(strNumber( /* "underline" */ 875)) default: prg.printEsc(strNumber( /* "overline" */ 874)) } case limitSwitch: if int32(chrCode) == limits { prg.printEsc(strNumber( /* "limits" */ 878)) } else if int32(chrCode) == noLimits { prg.printEsc(strNumber( /* "nolimits" */ 879)) } else { prg.printEsc(strNumber( /* "displaylimits" */ 1129)) } case mathStyle: prg.printStyle(int32(chrCode)) case above: switch chrCode { case overCode: prg.printEsc(strNumber( /* "over" */ 1148)) case atopCode: prg.printEsc(strNumber( /* "atop" */ 1149)) case delimitedCode + 0: prg.printEsc(strNumber( /* "abovewithdelims" */ 1150)) case delimitedCode + 1: prg.printEsc(strNumber( /* "overwithdelims" */ 1151)) case delimitedCode + 2: prg.printEsc(strNumber( /* "atopwithdelims" */ 1152)) default: prg.printEsc(strNumber( /* "above" */ 1147)) } case leftRight: if int32(chrCode) == leftNoad { prg.printEsc(strNumber( /* "left" */ 876)) } else { prg.printEsc(strNumber( /* "right" */ 877)) } case prefix: if int32(chrCode) == 1 { prg.printEsc(strNumber( /* "long" */ 1171)) } else if int32(chrCode) == 2 { prg.printEsc(strNumber( /* "outer" */ 1172)) } else { prg.printEsc(strNumber( /* "global" */ 1173)) } case def: if int32(chrCode) == 0 { prg.printEsc(strNumber( /* "def" */ 1174)) } else if int32(chrCode) == 1 { prg.printEsc(strNumber( /* "gdef" */ 1175)) } else if int32(chrCode) == 2 { prg.printEsc(strNumber( /* "edef" */ 1176)) } else { prg.printEsc(strNumber( /* "xdef" */ 1177)) } case let: if int32(chrCode) != normal { prg.printEsc(strNumber( /* "futurelet" */ 1192)) } else { prg.printEsc(strNumber( /* "let" */ 1191)) } case shorthandDef: switch chrCode { case charDefCode: prg.printEsc(strNumber( /* "chardef" */ 1193)) case mathCharDefCode: prg.printEsc(strNumber( /* "mathchardef" */ 1194)) case countDefCode: prg.printEsc(strNumber( /* "countdef" */ 1195)) case dimenDefCode: prg.printEsc(strNumber( /* "dimendef" */ 1196)) case skipDefCode: prg.printEsc(strNumber( /* "skipdef" */ 1197)) case muSkipDefCode: prg.printEsc(strNumber( /* "muskipdef" */ 1198)) default: prg.printEsc(strNumber( /* "toksdef" */ 1199)) } case charGiven: prg.printEsc(strNumber( /* "char" */ 513)) prg.printHex(int32(chrCode)) case mathGiven: prg.printEsc(strNumber( /* "mathchar" */ 524)) prg.printHex(int32(chrCode)) case defCode: if int32(chrCode) == catCodeBase { prg.printEsc(strNumber( /* "catcode" */ 415)) } else if int32(chrCode) == mathCodeBase { prg.printEsc(strNumber( /* "mathcode" */ 419)) } else if int32(chrCode) == lcCodeBase { prg.printEsc(strNumber( /* "lccode" */ 416)) } else if int32(chrCode) == ucCodeBase { prg.printEsc(strNumber( /* "uccode" */ 417)) } else if int32(chrCode) == sfCodeBase { prg.printEsc(strNumber( /* "sfcode" */ 418)) } else { prg.printEsc(strNumber( /* "delcode" */ 477)) } case defFamily: prg.printSize(int32(chrCode) - mathFontBase) case hyphData: if int32(chrCode) == 1 { prg.printEsc(strNumber( /* "patterns" */ 953)) } else { prg.printEsc(strNumber( /* "hyphenation" */ 941)) } case assignFontInt: if int32(chrCode) == 0 { prg.printEsc(strNumber( /* "hyphenchar" */ 1217)) } else { prg.printEsc(strNumber( /* "skewchar" */ 1218)) } case setFont: prg.print( /* "select font " */ 1226) prg.slowPrint(int32(prg.fontName[chrCode])) if prg.fontSize[chrCode] != prg.fontDsize[chrCode] { prg.print( /* " at " */ 741) prg.printScaled(prg.fontSize[chrCode]) prg.print( /* "pt" */ 397) } case setInteraction: switch chrCode { case batchMode: prg.printEsc(strNumber( /* "batchmode" */ 274)) case nonstopMode: prg.printEsc(strNumber( /* "nonstopmode" */ 275)) case scrollMode: prg.printEsc(strNumber( /* "scrollmode" */ 276)) default: prg.printEsc(strNumber( /* "errorstopmode" */ 1227)) } case inStream: if int32(chrCode) == 0 { prg.printEsc(strNumber( /* "closein" */ 1229)) } else { prg.printEsc(strNumber( /* "openin" */ 1228)) } case message: if int32(chrCode) == 0 { prg.printEsc(strNumber( /* "message" */ 1230)) } else { prg.printEsc(strNumber( /* "errmessage" */ 1231)) } case caseShift: if int32(chrCode) == lcCodeBase { prg.printEsc(strNumber( /* "lowercase" */ 1237)) } else { prg.printEsc(strNumber( /* "uppercase" */ 1238)) } case xray: switch chrCode { case showBoxCode: prg.printEsc(strNumber( /* "showbox" */ 1240)) case showTheCode: prg.printEsc(strNumber( /* "showthe" */ 1241)) case showListsCode: prg.printEsc(strNumber( /* "showlists" */ 1242)) default: prg.printEsc(strNumber( /* "show" */ 1239)) } case undefinedCs: prg.print( /* "undefined" */ 1249) case call: prg.print( /* "macro" */ 1250) case longCall: prg.printEsc(strNumber( /* "long macro" */ 1251)) case outerCall: prg.printEsc(strNumber( /* "outer macro" */ 1252)) case longOuterCall: prg.printEsc(strNumber( /* "long" */ 1171)) prg.printEsc(strNumber( /* "outer macro" */ 1252)) case endTemplate: prg.printEsc(strNumber( /* "outer endtemplate" */ 1253)) case extension: switch chrCode { case openNode: prg.printEsc(strNumber( /* "openout" */ 1285)) case writeNode: prg.printEsc(strNumber( /* "write" */ 594)) case closeNode: prg.printEsc(strNumber( /* "closeout" */ 1286)) case specialNode: prg.printEsc(strNumber( /* "special" */ 1287)) case immediateCode: prg.printEsc(strNumber( /* "immediate" */ 1288)) case setLanguageCode: prg.printEsc(strNumber( /* "setlanguage" */ 1289)) default: prg.print( /* "[unknown extension!]" */ 1290) } default: prg.print( /* "[unknown command code!]" */ 567) } } // procedure show_eqtb( n:halfword ); // begin if n1| appearing in |buffer[j.. // (j+l-1)]|. If the identifier is found, the corresponding hash table address // is returned. Otherwise, if the global variable |no_new_control_sequence| // is |true|, the dummy address |undefined_control_sequence| is returned. // Otherwise the identifier is inserted into the hash table and its location // is returned. func (prg *prg) idLookup(j, l int32) (r halfword) { // go here if you found it var ( h int32 // hash code d int32 // number of characters in incomplete current string p halfword // index in |hash| array k halfword // index in |buffer| array ) h = int32(prg.buffer[j]) for ii := j + 1; ii <= j+l-1; ii++ { k = halfword(ii) _ = k h = h + h + int32(prg.buffer[k]) for h >= hashPrime { h = h - hashPrime } } p = uint16(h + hashBase) // we start searching here; note that |0<=h 0 { if int32(prg.strStart[int32(*prg.hash[p-514].rh())+1])-int32(prg.strStart[*prg.hash[p-514].rh()]) == l { if prg.strEqBuf(*prg.hash[p-514].rh(), j) { goto found } } } if int32(*prg.hash[p-514].lh()) == 0 { if prg.noNewControlSequence { p = uint16(undefinedControlSequence) } else { // Insert a new control sequence after |p|, then make |p| point to it if int32(*prg.hash[p-514].rh()) > 0 { for { if int32(prg.hashUsed) == hashBase { prg.overflow(strNumber( /* "hash size" */ 503), hashSize) } // \xref[TeX capacity exceeded hash size][\quad hash size] prg.hashUsed = uint16(int32(prg.hashUsed) - 1) if int32(*prg.hash[prg.hashUsed-514].rh()) == 0 { break } } // search for an empty location in |hash| *prg.hash[p-514].lh() = prg.hashUsed p = prg.hashUsed } { if int32(prg.poolPtr)+l > poolSize { prg.overflow(strNumber( /* "pool size" */ 257), poolSize-int32(prg.initPoolPtr)) } /* \xref[TeX capacity exceeded pool size][\quad pool size] */ } d = int32(prg.poolPtr) - int32(prg.strStart[prg.strPtr]) for int32(prg.poolPtr) > int32(prg.strStart[prg.strPtr]) { prg.poolPtr = uint16(int32(prg.poolPtr) - 1) prg.strPool[int32(prg.poolPtr)+l] = prg.strPool[prg.poolPtr] } // move current string up to make room for another for ii := j; ii <= j+l-1; ii++ { k = halfword(ii) _ = k prg.strPool[prg.poolPtr] = prg.buffer[k] prg.poolPtr = uint16(int32(prg.poolPtr) + 1) } *prg.hash[p-514].rh() = prg.makeString() prg.poolPtr = uint16(int32(prg.poolPtr) + d) // cs_count:= cs_count+1 ; [ ] } goto found } p = *prg.hash[p-514].lh() } found: r = p return r } // 264. // tangle:pos tex.web:5631:3: // We need to put \TeX's “primitive” control sequences into the hash // table, together with their command code (which will be the |eq_type|) // and an operand (which will be the |equiv|). The |primitive| procedure // does this, in a way that no \TeX\ user can. The global value |cur_val| // contains the new |eqtb| pointer after |primitive| has acted. func (prg *prg) primitive(s strNumber, c quarterword, o halfword) { var ( k poolPointer // index into |str_pool| j smallNumber // index into |buffer| l smallNumber // length of the string ) if int32(s) < 256 { prg.curVal = int32(s) + singleBase } else { k = prg.strStart[s] l = byte(int32(prg.strStart[int32(s)+1]) - int32(k)) // we will move |s| into the (empty) |buffer| for ii := int32(0); ii <= int32(l)-1; ii++ { j = smallNumber(ii) _ = j prg.buffer[j] = prg.strPool[int32(k)+int32(j)] } prg.curVal = int32(prg.idLookup(0, int32(l))) // |no_new_control_sequence| is |false| { prg.strPtr = uint16(int32(prg.strPtr) - 1) prg.poolPtr = prg.strStart[prg.strPtr] } *prg.hash[prg.curVal-514].rh() = s // we don't want to have the string twice } *(*prg.eqtb[prg.curVal-1].hh()).b1() = byte(levelOne) *(*prg.eqtb[prg.curVal-1].hh()).b0() = c *(*prg.eqtb[prg.curVal-1].hh()).rh() = o } // 268. \[19] Saving and restoring equivalents // tangle:pos tex.web:5813:43: // The nested structure provided by `$\.[\char'173]\ldots\.[\char'175]$' groups // in \TeX\ means that |eqtb| entries valid in outer groups should be saved // and restored later if they are overridden inside the braces. When a new |eqtb| // value is being assigned, the program therefore checks to see if the previous // entry belongs to an outer level. In such a case, the old value is placed // on the |save_stack| just before the new value enters |eqtb|. At the // end of a grouping level, i.e., when the right brace is sensed, the // |save_stack| is used to restore the outer values, and the inner ones are // destroyed. // // Entries on the |save_stack| are of type |memory_word|. The top item on // this stack is |save_stack[p]|, where |p=save_ptr-1|; it contains three // fields called |save_type|, |save_level|, and |save_index|, and it is // interpreted in one of four ways: // // \yskip\hangg 1) If |save_type(p)=restore_old_value|, then // |save_index(p)| is a location in |eqtb| whose current value should // be destroyed at the end of the current group and replaced by |save_stack[p-1]|. // Furthermore if |save_index(p)>=int_base|, then |save_level(p)| // should replace the corresponding entry in |xeq_level|. // // \yskip\hangg 2) If |save_type(p)=restore_zero|, then |save_index(p)| // is a location in |eqtb| whose current value should be destroyed at the end // of the current group, when it should be // replaced by the value of |eqtb[undefined_control_sequence]|. // // \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)| // is a code explaining what kind of group we were previously in, and // |save_index(p)| points to the level boundary word at the bottom of // the entries for that group. // 270. // tangle:pos tex.web:5892:3: // The global variable |cur_group| keeps track of what sort of group we are // currently in. Another global variable, |cur_boundary|, points to the // topmost |level_boundary| word. And |cur_level| is the current depth of // nesting. The routines are designed to preserve the condition that no entry // in the |save_stack| or in |eqtb| ever has a level greater than |cur_level|. // 273. // tangle:pos tex.web:5915:3: // The following macro is used to test if there is room for up to six more // entries on |save_stack|. By making a conservative test like this, we can // get by with testing for overflow in only a few places. // 274. // tangle:pos tex.web:5925:3: // Procedure |new_save_level| is called when a group begins. The // argument is a group identification code like `|hbox_group|'. After // calling this routine, it is safe to put five more entries on |save_stack|. // // In some cases integer-valued items are placed onto the // |save_stack| just below a |level_boundary| word, because this is a // convenient place to keep information that is supposed to “pop up” just // when the group has finished. // For example, when `\.[\\hbox to 100pt]\grp' is being treated, the 100pt // dimension is stored on |save_stack| just before |new_save_level| is // called. // // We use the notation |saved(k)| to stand for an integer item that // appears in location |save_ptr+k| of the save stack. func (prg *prg) newSaveLevel(c groupCode) { if int32(prg.savePtr) > int32(prg.maxSaveStack) { prg.maxSaveStack = prg.savePtr if int32(prg.maxSaveStack) > saveSize-6 { prg.overflow(strNumber( /* "save size" */ 541), saveSize) } /* \xref[TeX capacity exceeded save size][\quad save size] */ } *(*prg.saveStack[prg.savePtr].hh()).b0() = byte(levelBoundary) *(*prg.saveStack[prg.savePtr].hh()).b1() = prg.curGroup *(*prg.saveStack[prg.savePtr].hh()).rh() = prg.curBoundary if int32(prg.curLevel) == maxQuarterword { prg.overflow(strNumber( /* "grouping levels" */ 542), maxQuarterword-minQuarterword) } // quit if |(cur_level+1)| is too big to be stored in |eqtb| prg.curBoundary = prg.savePtr prg.curLevel = byte(int32(prg.curLevel) + 1) prg.savePtr = uint16(int32(prg.savePtr) + 1) prg.curGroup = c } // 275. // tangle:pos tex.web:5953:3: // Just before an entry of |eqtb| is changed, the following procedure should // be called to update the other data structures properly. It is important // to keep in mind that reference counts in |mem| include references from // within |save_stack|, so these counts must be handled carefully. // \xref[reference counts] func (prg *prg) eqDestroy(w memoryWord) { // gets ready to forget |w| var ( q halfword // |equiv| field of |w| ) switch *(*w.hh()).b0() { case call, longCall, outerCall, longOuterCall: prg.deleteTokenRef(*(*w.hh()).rh()) case glueRef: prg.deleteGlueRef(*(*w.hh()).rh()) case shapeRef: q = *(*w.hh()).rh() // we need to free a \.[\\parshape] block if int32(q) != 0 { prg.freeNode(q, halfword(int32(*(*prg.mem[q].hh()).lh())+int32(*(*prg.mem[q].hh()).lh())+1)) } // such a block is |2n+1| words long, where |n=info(q)| case boxRef: prg.flushNodeList(*(*w.hh()).rh()) default: } } // 276. // tangle:pos tex.web:5972:3: // To save a value of |eqtb[p]| that was established at level |l|, we // can use the following subroutine. func (prg *prg) eqSave(p halfword, l quarterword) { if int32(prg.savePtr) > int32(prg.maxSaveStack) { prg.maxSaveStack = prg.savePtr if int32(prg.maxSaveStack) > saveSize-6 { prg.overflow(strNumber( /* "save size" */ 541), saveSize) } /* \xref[TeX capacity exceeded save size][\quad save size] */ } if int32(l) == levelZero { *(*prg.saveStack[prg.savePtr].hh()).b0() = byte(restoreZero) } else { prg.saveStack[prg.savePtr] = prg.eqtb[p-1] prg.savePtr = uint16(int32(prg.savePtr) + 1) *(*prg.saveStack[prg.savePtr].hh()).b0() = byte(restoreOldValue) } *(*prg.saveStack[prg.savePtr].hh()).b1() = l *(*prg.saveStack[prg.savePtr].hh()).rh() = p prg.savePtr = uint16(int32(prg.savePtr) + 1) } // 277. // tangle:pos tex.web:5984:3: // The procedure |eq_define| defines an |eqtb| entry having specified // |eq_type| and |equiv| fields, and saves the former value if appropriate. // This procedure is used only for entries in the first four regions of |eqtb|, // i.e., only for entries that have |eq_type| and |equiv| fields. // After calling this routine, it is safe to put four more entries on // |save_stack|, provided that there was room for four more entries before // the call, since |eq_save| makes the necessary test. func (prg *prg) eqDefine(p halfword, t quarterword, e halfword) { if int32(*(*prg.eqtb[p-1].hh()).b1()) == int32(prg.curLevel) { prg.eqDestroy(prg.eqtb[p-1]) } else if int32(prg.curLevel) > levelOne { prg.eqSave(p, *(*prg.eqtb[p-1].hh()).b1()) } *(*prg.eqtb[p-1].hh()).b1() = prg.curLevel *(*prg.eqtb[p-1].hh()).b0() = t *(*prg.eqtb[p-1].hh()).rh() = e } // 278. // tangle:pos tex.web:5999:3: // The counterpart of |eq_define| for the remaining (fullword) positions in // |eqtb| is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all // |p|, a `|restore_zero|' will never be used in this case. func (prg *prg) eqWordDefine(p halfword, w int32) { if int32(prg.xeqLevel[p-5263]) != int32(prg.curLevel) { prg.eqSave(p, prg.xeqLevel[p-5263]) prg.xeqLevel[p-5263] = prg.curLevel } *prg.eqtb[p-1].int() = w } // 279. // tangle:pos tex.web:6010:3: // The |eq_define| and |eq_word_define| routines take care of local definitions. // \xref[global definitions] // Global definitions are done in almost the same way, but there is no need // to save old values, and the new value is associated with |level_one|. func (prg *prg) geqDefine(p halfword, t quarterword, e halfword) { prg.eqDestroy(prg.eqtb[p-1]) *(*prg.eqtb[p-1].hh()).b1() = byte(levelOne) *(*prg.eqtb[p-1].hh()).b0() = t *(*prg.eqtb[p-1].hh()).rh() = e } func (prg *prg) geqWordDefine(p halfword, w int32) { *prg.eqtb[p-1].int() = w prg.xeqLevel[p-5263] = byte(levelOne) } // 280. // tangle:pos tex.web:6025:3: // Subroutine |save_for_after| puts a token on the stack for save-keeping. func (prg *prg) saveForAfter(t halfword) { if int32(prg.curLevel) > levelOne { if int32(prg.savePtr) > int32(prg.maxSaveStack) { prg.maxSaveStack = prg.savePtr if int32(prg.maxSaveStack) > saveSize-6 { prg.overflow(strNumber( /* "save size" */ 541), saveSize) } /* \xref[TeX capacity exceeded save size][\quad save size] */ } *(*prg.saveStack[prg.savePtr].hh()).b0() = byte(insertToken) *(*prg.saveStack[prg.savePtr].hh()).b1() = byte(levelZero) *(*prg.saveStack[prg.savePtr].hh()).rh() = t prg.savePtr = uint16(int32(prg.savePtr) + 1) } } // \2 func (prg *prg) unsave() { var ( p halfword // position to be restored l quarterword // saved level, if in fullword regions of |eqtb| t halfword // saved value of |cur_tok| ) if int32(prg.curLevel) > levelOne { prg.curLevel = byte(int32(prg.curLevel) - 1) // Clear off top level from |save_stack| for true { prg.savePtr = uint16(int32(prg.savePtr) - 1) if int32(*(*prg.saveStack[prg.savePtr].hh()).b0()) == levelBoundary { goto done } p = *(*prg.saveStack[prg.savePtr].hh()).rh() if int32(*(*prg.saveStack[prg.savePtr].hh()).b0()) == insertToken { t = prg.curTok prg.curTok = p prg.backInput() prg.curTok = t } else { if int32(*(*prg.saveStack[prg.savePtr].hh()).b0()) == restoreOldValue { l = *(*prg.saveStack[prg.savePtr].hh()).b1() prg.savePtr = uint16(int32(prg.savePtr) - 1) } else { prg.saveStack[prg.savePtr] = prg.eqtb[undefinedControlSequence-1] } // Store \(s)|save_stack[save_ptr]| in |eqtb[p]|, unless |eqtb[p]| holds a global value if int32(p) < intBase { if int32(*(*prg.eqtb[p-1].hh()).b1()) == levelOne { prg.eqDestroy(prg.saveStack[prg.savePtr]) // destroy the saved value // if eqtb[int_base+ tracing_restores_code].int >0 then restore_trace(p,["retaining"=]544); [ ] } else { prg.eqDestroy(prg.eqtb[p-1]) // destroy the current value prg.eqtb[p-1] = prg.saveStack[prg.savePtr] // restore the saved value // if eqtb[int_base+ tracing_restores_code].int >0 then restore_trace(p,["restoring"=]545); [ ] } } else if int32(prg.xeqLevel[p-5263]) != levelOne { prg.eqtb[p-1] = prg.saveStack[prg.savePtr] prg.xeqLevel[p-5263] = l // if eqtb[int_base+ tracing_restores_code].int >0 then restore_trace(p,["restoring"=]545); [ ] } else { } } } done: prg.curGroup = *(*prg.saveStack[prg.savePtr].hh()).b1() prg.curBoundary = *(*prg.saveStack[prg.savePtr].hh()).rh() } else { prg.confusion(strNumber( /* "curlevel" */ 543)) } // |unsave| is not used when |cur_group=bottom_level| // \xref[this can't happen curlevel][\quad curlevel] } // 288. // tangle:pos tex.web:6129:3: // The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag| // for magnification. func (prg *prg) prepareMag() { if prg.magSet > 0 && *prg.eqtb[intBase+magCode-1].int() != prg.magSet { { if int32(prg.interaction) == errorStopMode { } prg.printNl(strNumber( /* "! " */ 262)) prg.print( /* "Incompatible magnification (" */ 547) } prg.printInt(*prg.eqtb[intBase+magCode-1].int()) // \xref[Incompatible magnification] prg.print( /* ");" */ 548) prg.printNl(strNumber( /* " the previous value will be retained" */ 549)) { prg.helpPtr = 2 prg.helpLine[1] = /* "I can handle only one magnification ratio per job. So I've" */ 550 prg.helpLine[0] = /* "reverted to the magnification you used earlier on this run." */ 551 } prg.intError(prg.magSet) prg.geqWordDefine(halfword(intBase+magCode), prg.magSet) // |mag:=mag_set| } if *prg.eqtb[intBase+magCode-1].int() <= 0 || *prg.eqtb[intBase+magCode-1].int() > 32768 { { if int32(prg.interaction) == errorStopMode { } prg.printNl(strNumber( /* "! " */ 262)) prg.print( /* "Illegal magnification has been changed to 1000" */ 552) } // \xref[Illegal magnification...] { prg.helpPtr = 1 prg.helpLine[0] = /* "The magnification ratio must be between 1 and 32768." */ 553 } prg.intError(*prg.eqtb[intBase+magCode-1].int()) prg.geqWordDefine(halfword(intBase+magCode), 1000) } prg.magSet = *prg.eqtb[intBase+magCode-1].int() } // 289. \[20] Token lists // tangle:pos tex.web:6151:22: // A \TeX\ token is either a character or a control sequence, and it is // \xref[token] // 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=$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. // // A token |t| represents a |left_brace| command if and only if // |t= call { prg.printChar(asciiCode(':')) prg.printLn() prg.tokenShow(prg.curChr) } else if int32(prg.curCmd) == topBotMark { prg.printChar(asciiCode(':')) prg.printLn() prg.tokenShow(prg.curMark[prg.curChr]) } } // 299. // tangle:pos tex.web:6419:3: // Here is a procedure that displays the current command. func (prg *prg) showCurCmdChr() { prg.beginDiagnostic() prg.printNl(strNumber('{')) if int32(prg.curList.modeField) != int32(prg.shownMode) { prg.printMode(int32(prg.curList.modeField)) prg.print( /* ": " */ 568) prg.shownMode = prg.curList.modeField } prg.printCmdChr(prg.curCmd, prg.curChr) prg.printChar(asciiCode('}')) prg.endDiagnostic(false) } // \2 func (prg *prg) showContext() { var ( oldSetting/* 0..maxSelector */ byte // saved |selector| setting nn int32 // number of contexts shown so far, less one bottomLine bool // have we reached the final context to be shown? // Local variables for formatting calculations i/* 0..bufSize */ uint16 // index into |buffer| j/* 0..bufSize */ uint16 // end of current line in |buffer| l/* 0..halfErrorLine */ byte // length of descriptive information on line 1 m int32 // context information gathered for line 2 n/* 0..errorLine */ byte // length of line 1 p int32 // starting or ending place in |trick_buf| q int32 // temporary index ) prg.basePtr = prg.inputPtr prg.inputStack[prg.basePtr] = prg.curInput // store current state nn = -1 bottomLine = false for true { prg.curInput = prg.inputStack[prg.basePtr] // enter into the context if int32(prg.curInput.stateField) != tokenList { if int32(prg.curInput.nameField) > 17 || int32(prg.basePtr) == 0 { bottomLine = true } } if int32(prg.basePtr) == int32(prg.inputPtr) || bottomLine || nn < *prg.eqtb[intBase+errorContextLinesCode-1].int() { if int32(prg.basePtr) == int32(prg.inputPtr) || int32(prg.curInput.stateField) != tokenList || int32(prg.curInput.indexField) != backedUp || int32(prg.curInput.locField) != 0 { prg.tally = 0 // get ready to count characters oldSetting = prg.selector if int32(prg.curInput.stateField) != tokenList { if int32(prg.curInput.nameField) <= 17 { if int32(prg.curInput.nameField) == 0 { if int32(prg.basePtr) == 0 { prg.printNl(strNumber( /* "<*>" */ 574)) } else { prg.printNl(strNumber( /* " " */ 575)) } } else { prg.printNl(strNumber( /* "')) } } else { prg.printNl(strNumber( /* "l." */ 577)) prg.printInt(prg.line) } prg.printChar(asciiCode(' ')) // Pseudoprint the line { l = byte(prg.tally) prg.tally = 0 prg.selector = byte(pseudo) prg.trickCount = 1000000 } if int32(prg.buffer[prg.curInput.limitField]) == *prg.eqtb[intBase+endLineCharCode-1].int() { j = prg.curInput.limitField } else { j = uint16(int32(prg.curInput.limitField) + 1) } // determine the effective end of the line if int32(j) > 0 { for ii := int32(prg.curInput.startField); ii <= int32(j)-1; ii++ { i = uint16(ii) _ = i if int32(i) == int32(prg.curInput.locField) { prg.firstCount = prg.tally prg.trickCount = prg.tally + 1 + errorLine - halfErrorLine if prg.trickCount < errorLine { prg.trickCount = errorLine } } prg.print(int32(prg.buffer[i])) } } } else { switch prg.curInput.indexField { case parameter: prg.printNl(strNumber( /* " " */ 578)) case uTemplate, vTemplate: prg.printNl(strNumber( /* "