// TEXRS — PRIMITIVE REFERENCE

texrs v0.6.0 · TeX on fusevm · mouth → expander → command stream → bytecode → Cranelift JIT · MIT · in active development

Docs Report GitHub
// Color scheme

>_PRIMITIVE REFERENCE

Every primitive texrs carries, what it does, and where it happens — at compile time while lowering, or at run time on the VM. A primitive not on this page is not implemented; BUGS.md says so explicitly for the ones that are commonly reached for.

166
Primitives
10
Chapters
256
Count registers
53
Command-line options

Category codes

PrimitiveWhat it doesSyntax and example
\catcodeSet or read a character's category code. A character's category is a mutable table entry, not a property of the language: texrs starts from INITEX's sparse defaults, where { is an ordinary character until something makes it a group opener. Takes effect at COMPILE time, because it changes how the rest of the file reads.
\catcode`\X=N
\catcode`\{=1 \catcode`\}=2 \catcode`\#=6
\message{now braces group}
^^XControl-character notation (tex.web §352), in two forms decided by what follows: ^^ and two LOWERCASE hex digits is that hex code, while ^^ and anything else is one character shifted by 64. So ^^41 is A and ^^4a is J, but ^^4A is tAA is not a lowercase hex digit, so the shift applies to the 4 alone. The substitution belongs to the input processor (§353), which runs before anything is classified, so it applies inside a control sequence name too: plain.tex writes \catcode\^^K=7. This is also how a line end is written inside a macro body.
^^M   % carriage return
^^I   % tab
^^41  % the hex form: A
`A character code as a number, for a register assignment or a comparison: a backtick followed by a character is that character's code, and the backslash before the character is optional unless it is a control sequence.
\count1=`\A
\message{\the\count1}   % => 65

Macro definition

PrimitiveWhat it doesSyntax and example
\defDefine a macro. Parameters may be undelimited (#1) or delimited (\def\pair#1,#2.{...}, where the argument is whatever precedes the delimiter), and ## is a literal parameter character. The parameter text is validated as tex.web §476 validates it: every # must be followed by a digit, and the digits must run consecutively. Takes effect at COMPILE time. The name may be an ACTIVE CHARACTER as well as a control sequence (tex.web §1215): \catcode\~=13 \def~{...} defines ~ itself, and an active ~ and the control sequence \~ stay different things.
\def\name<parameter text>{<body>}
\def\greet#1{HELLO-#1}
\def\pair#1,#2.{[#1|#2]}
\message{\greet{WORLD}}   % => HELLO-WORLD
\message{\pair 1,2.}      % => [1|2]
\gdefDefine globally: the definition survives the enclosing group, where a \def inside braces is undone at the closing brace.
\gdef\name{<body>}
{\gdef\v{IN}}
\message{\v}   % => IN
\edefDefine with the body expanded NOW, so a register read is frozen at definition time and a later assignment cannot move it. tex.web §366 expands the body while READING it, which decides a conditional in it there and turns \the into §478's characters. texrs does that whenever it can prove the registers the body reads — a register nothing has assigned yet is at INITEX's zero, which is what the compiler writes into every slot — and otherwise falls back to snapshotting the \the\count read into a scratch register (taken from the top of the count range) and carrying the conditional. The fallback is the case BUGS.md records: a conditional over a register the program HAS written is still decided at use time.
\edef\name{<body>}
\count1=1
\edef\frozen{\the\count1}
\count1=2
\message{\frozen}   % => 1
\edef\case{\ifcase\count2 ZERO\else OTHER\fi}
\count2=5
\message{\case}      % => ZERO
\xdefA global \edef: the body is expanded now and the definition survives the enclosing group.
\xdef\name{<body>}
{\xdef\frozen{\the\count1}}
\letGive a control sequence another's CURRENT meaning, not a reference to it: redefining the source afterwards does not change the alias.
\let\alias=\source
\def\v{ONE}
\let\w=\v
\def\v{TWO}
\message{\w}   % => ONE
\futureletLook one token past the next without eating either: \futurelet\a\b\c gives \a the meaning of \c and then puts \b and \c back, so the stream is exactly as it was (tex.web §1221). That non-destructive peek is what LaTeX's \@ifnextchar is built from, and so is every optional argument in the language. A control sequence let to a character MEANS that character, so \ifx\next[ compares true — which is the comparison the whole idiom rests on.
\futurelet\next<token><token>
\def\peek{\futurelet\next\decide}
\def\decide{\ifx\next[ OPTIONAL\else PLAIN\fi}
\globalPrefix making the following assignment global, so it survives the enclosing group.
\global\count1=5
\global\def\v{OUT}
\longA definition prefix: the macro's arguments may contain \par. Without it a paragraph break inside an argument is a runaway, which is TeX's guard against a missing closing brace swallowing the rest of a document. texrs records the prefix and does not yet enforce the restriction it lifts.
\long\def\note#1{[#1]}
\outerA definition prefix: the macro may not then appear in an argument, in a group being scanned as text, or in skipped conditional text. It is an error-detection feature -- plain.tex marks its sectioning macros \outer so a missing brace is caught at the next section rather than at the end of the file. texrs records the prefix and does not police the restriction, so a use tex forbids is accepted here; tests/cases/outer_forbidden_use.tex pins the difference. Prefixes are part of the meaning, so \ifx tells a prefixed definition from a bare one.
\outer\def\chapter{...}
\protectedA definition prefix: the macro does not expand inside an \edef, it survives as itself and runs when the result does. Redefining it afterwards therefore changes what the \edef'd macro produces, which is the observable difference. An eTeX primitive, and the one LaTeX leans on to keep a fragile command safe in a moving argument.
\protected\def\note#1{[#1]}

Expansion

PrimitiveWhat it doesSyntax and example
\csnameBuild a control sequence out of the characters up to \endcsname, so a macro can name another macro. A name with no meaning becomes \relax, as in tex.
\csname <characters>\endcsname
\def\greeting{HI}
\def\name{greeting}
\message{\csname \name\endcsname}   % => HI
\endcsnameTerminate a \csname. It is an error to reach one without an open \csname.
\csname foo\endcsname
\stringPrint a control sequence as text, escape character included — the inverse of \csname.
\string\cs
\message{\string\undefined}   % => \undefined
\meaningWhat a token MEANS, as characters (tex.web §296's print_meaning). A macro reads back as macro: then its parameter text, -> and its body, with \long, \outer and \protected run together in front; a \chardef constant reads back as \char"41; a register name reads back as the register it stands for; a character carries the NAME of its category, so \meaning A is the letter A. A name nothing has defined is undefined. Expandable, like \string, so it works inside a \message.
\meaning<token>
\def\a#1{[#1]}
\message{\meaning\a}   % => macro:#1->[#1]
\uppercaseRead the following group WITHOUT expanding it, replace every character by its \uccode and put the result back to be read again (tex.web §1288). A zero code means the character has no case and stands, so digits and punctuation come through untouched, and the category code travels with the character unchanged — which is what LaTeX's \MakeUppercase is built on.
\uppercase{<text>}
\uppercase{\message{abc 123}}   % => ABC 123
\lowercaseThe same as \uppercase over the \lccode table: the group is read unexpanded, every character is replaced by its lowercase code, and the result is read again with its category codes intact.
\lowercase{<text>}
\lowercase{\message{ABC dEf}}   % => abc def
\theThe value of a register, as characters. The read happens at RUN time on the VM, which is why a \message containing \the\count0 follows a later assignment.
\the\count<N>
\count1=12
\message{count=\the\count1}   % => count=12
\numberA scanned number, as characters, with no leading zeros or plus sign. The scan keeps reading (and expanding) until something that cannot be part of a number stops it, so a following letter needs a terminating space.
\number<number>
\message{\number0042}   % => 42
\expandafterOne token of lookahead: hold the next token back, expand what follows it once, then put the held token in front of the result.
\expandafter<token><token>
\def\a{\b}
\def\b{DEEP}
\message{\expandafter\string\a}   % => \b
\noexpandSuppress expansion of the next token for one step — the token is used for its own sake rather than its meaning.
\noexpand<token>
\edef\keep{\noexpand\later}
\messageWrite to the terminal. This is texrs's parity contract for the milestone: two host builtins, one appending a rendered piece and one flushing the assembled string, so a message reads its registers at run time.
\message{<text>}
\message{HELLO-WORLD}   % => (./file.tex HELLO-WORLD )
\relaxDo nothing. Accepted so a document can stop a number scan, or fill a slot that needs a token but no action — which is also what \csname makes of a name with no meaning.
\relax
\parEnd a paragraph. A blank line produces one, which is why the line scanner tracks its state, and in a typesetting run it reaches the page: the breaker starts a fresh paragraph, so the line before it ends where the paragraph does however short that leaves it.
\par
\ignorespacesSkip the spaces that follow (tex.web §1060). It is a mouth-level effect: the spaces are read and dropped, so what comes next is the first non-blank token. That is the whole of it here — the horizontal-mode half needs a stomach.
\ignorespaces
\def\a{\ignorespaces}
\a   X   % the spaces before X are gone
\endStop the run, and ship what it built. A document that set text comes out as Output written on NAME.pdf (N pages, N bytes).; one that only sent messages reports No pages of output., which is what real tex says for every case in the parity corpus and why the contract for those is still the \message stream.
\end
\detokenizeThe tokens of {...} written as text, by the token-list rule: a control word carries a trailing space, a one-character control sequence does not. Nothing in the group expands. An eTeX primitive.
\message{\detokenize{\a b}}   % => \a b
\csstring\string without the escape character: \csstring\foo is foo where \string\foo is \foo. A LuaTeX primitive.
\message{\csstring\foo}   % => foo
\UcharThe character with the given code: \Uchar65 is A. A LuaTeX primitive, and the one that reaches past 255 -- texrs reads characters rather than bytes, so it carries the whole range.
\message{\Uchar65\Uchar97}   % => Aa
\expandedExpand the group's contents completely, here and now, and put the result back. Inside an \edef it is the wrapper coming off, since everything there expands anyway; in running text it forces the expansion a macro would otherwise have deferred. An eTeX primitive.
\message{\expanded{\body}}
\unexpandedThe opposite: the group's tokens are used as they stand. Inside an \edef they survive as TOKENS, so a macro among them is called when the body runs rather than when it is defined -- which is where this and \expanded part company. In a message the two render alike, by the token-list rule. An eTeX primitive.
\edef\keep{\unexpanded{\later}}
\begincsname\csname that does not define what it does not find: an unknown name expands to nothing, where \csname would make it \relax and leave it defined ever after. A LuaTeX primitive, and the one that makes "is this defined?" answerable without changing the answer.
\begincsname maybe\endcsname

Conditionals

PrimitiveWhat it doesSyntax and example
\ifnumCompare two numbers with <, = or >. It tests run-time state, so it lowers to a real branch — a comparison plus a jump — rather than a decision taken while walking a tree.
\ifnum<number><rel><number> <true>\else <false>\fi
\count1=5
\message{\ifnum\count1>3 BIG\else SMALL\fi}   % => BIG
\ifoddTrue when a number is odd. Reads run-time state, so it lowers to a real branch rather than folding. Negative odd numbers are odd: the test is on the remainder being non-zero, not on its sign.
\ifodd<number> <true>\else <false>\fi
\message{\ifodd\count1 ODD\else EVEN\fi}
\ifcaseSwitch on a number: the first case is 0, each \or starts the next, and \else catches everything past the last. Lowers to a real branch. A selector past the last \or with no \else selects nothing and the run continues. DIVERGENCE: a NEGATIVE selector takes case 0 here where tex takes \else\ifcase -1 ZERO\else DEFAULT\fi prints ZERO rather than DEFAULT. Pinned by tests/cases/cond_ifcase_negative.tex.
\ifcase<number> <0>\or <1>\or <2>\else <other>\fi
\count1=2
\message{\ifcase\count1 ZERO\or ONE\or TWO\else MANY\fi}   % => TWO
\iftrueConstant truth. Decidable without the VM, so it is FOLDED while lowering and the untaken arm is never emitted — it does not merely jump over that code, it never emits it. This is why a \def inside the untaken arm of an \iftrue is safe where the same \def inside a run-time conditional is not; see def_in_conditional_arm.tex., so it is FOLDED while lowering and the untaken arm is never emitted.
\iftrue <true>\else <false>\fi
\iffalseConstant falsity. Folded while lowering, like \iftrue, so the taken arm is the only code that reaches the chunk.
\iffalse <true>\else <false>\fi
\ifxCompare two meanings: two macros are equal when their parameter texts and bodies match, and two primitives when they are the same primitive. Decidable from the macro table alone, so it is folded while lowering.
\ifx<token><token> <true>\else <false>\fi
\def\a{X}\def\b{X}
\message{\ifx\a\b SAME\else DIFF\fi}   % => SAME
\ifCompare two character codes after expansion. A control sequence compares equal to any OTHER control sequence, because neither has a character code to compare — that is tex.web §506's rule, not a shortcut. Both sides are expanded first, so \if\a\b tests what the macros produce, not their names.
\if<token><token> <true>\else <false>\fi
\ifdefinedTrue when a control sequence has a meaning. Decidable from the macro table alone, so it is FOLDED while lowering and only the taken arm is emitted. An undefined name is false rather than an error, which is the one place an undefined control sequence is not a divergence here.
\ifdefined<token> <true>\else <false>\fi
\elseStart the false arm of a conditional. Both arms are collected as token runs and lowered, so the untaken one is real code the VM jumps over rather than a subtree nobody walked.
\else
\orStart the next case of an \ifcase: the case before the first \or is 0, and each \or moves to the next.
\or
\fiEnd a conditional. A control word swallows the space after it, so \fi X prints X with no leading space.
\fi
\ifcatRECOGNISED BUT NOT EVALUATED. Compares category codes; texrs skips the construct correctly so an unbalanced branch cannot confuse the scanner, but cannot decide it. Reaching one stops the run with ! Unsupported conditional \NAME. and exit status 1; SKIPPING one inside an untaken branch is correct, because the skipper counts it for nesting.
\ifcat<token><token> <true>\else <false>\fi
\ifdimCompare two dimensions. tex.web §503 shares the comparison with \ifnum and differs only in the scanner, and a dimension is an integer in a slot here, so this lowers to the same run-time branch \ifnum does rather than being recognised and skipped.
\ifdim<dimen><rel><dimen> <true>\else <false>\fi
\dimen1=2pt
\ifdim\dimen1>1pt \message{[WIDER]}\else \message{[NARROWER]}\fi   % => [WIDER]
\ifvoidIs box register N empty? The register number is read as tex.web §433 reads one, and the answer is that the register is VOID — which is what it is: texrs has no \setbox, §462's box(n) is null for a register nothing has filled, and all 256 of them are in that state. So this is decided while lowering, in running text and inside a \message alike, and agrees with tex for every document that fills no box. The reference tex loads plain.tex, which fills \box0 and puts an \hbox in \box11, so those two disagree the way \count0 does.
\ifvoid<N> <true>\else <false>\fi
\ifvoid200 \message{[EMPTY]}\else \message{[FULL]}\fi   % => [EMPTY]
\ifhboxDoes box register N hold an \hbox? The register number is read as tex.web §433 reads one, and the answer is that the register is VOID — which is what it is: texrs has no \setbox, §462's box(n) is null for a register nothing has filled, and all 256 of them are in that state. So this is decided while lowering, in running text and inside a \message alike, and agrees with tex for every document that fills no box. The reference tex loads plain.tex, which fills \box0 and puts an \hbox in \box11, so those two disagree the way \count0 does.
\ifhbox<N> <true>\else <false>\fi
\ifhbox200 \message{[H]}\else \message{[NO]}\fi   % => [NO]
\ifvboxDoes box register N hold a \vbox? The register number is read as tex.web §433 reads one, and the answer is that the register is VOID — which is what it is: texrs has no \setbox, §462's box(n) is null for a register nothing has filled, and all 256 of them are in that state. So this is decided while lowering, in running text and inside a \message alike, and agrees with tex for every document that fills no box. The reference tex loads plain.tex, which fills \box0 and puts an \hbox in \box11, so those two disagree the way \count0 does.
\ifvbox<N> <true>\else <false>\fi
\ifvbox200 \message{[V]}\else \message{[NO]}\fi   % => [NO]
\ifvmodeRECOGNISED BUT NOT EVALUATED: modes belong to the stomach. Reaching one stops the run with ! Unsupported conditional \NAME. and exit status 1; SKIPPING one inside an untaken branch is correct, because the skipper counts it for nesting.
\ifvmode <true>\else <false>\fi
\ifhmodeRECOGNISED BUT NOT EVALUATED: modes belong to the stomach. Reaching one stops the run with ! Unsupported conditional \NAME. and exit status 1; SKIPPING one inside an untaken branch is correct, because the skipper counts it for nesting.
\ifhmode <true>\else <false>\fi
\ifmmodeRECOGNISED BUT NOT EVALUATED: modes belong to the stomach. Reaching one stops the run with ! Unsupported conditional \NAME. and exit status 1; SKIPPING one inside an untaken branch is correct, because the skipper counts it for nesting.
\ifmmode <true>\else <false>\fi
\ifinnerRECOGNISED BUT NOT EVALUATED: modes belong to the stomach. Reaching one stops the run with ! Unsupported conditional \NAME. and exit status 1; SKIPPING one inside an untaken branch is correct, because the skipper counts it for nesting.
\ifinner <true>\else <false>\fi
\ifeofRECOGNISED BUT NOT EVALUATED: there is no file I/O yet. Reaching one stops the run with ! Unsupported conditional \NAME. and exit status 1; SKIPPING one inside an untaken branch is correct, because the skipper counts it for nesting.
\ifeof<N> <true>\else <false>\fi
\ifcsnameIs the name built from these characters defined? etex.ch's if_cs_code reads them exactly as \csname does and then looks the name up with no_new_control_sequence still true, so a name it does not find is NOT entered — which is the whole difference from \csname, whose lookup DEFINES what it misses as \relax (tex.web §372) and so changes the answer for every later ask. Decided while lowering, in running text and inside a \message body alike, because the macro table is a frontend fact. LaTeX's \@ifundefined is built on this.
\ifcsname <characters>\endcsname <true>\else <false>\fi
\def\foo{F}
\ifcsname foo\endcsname \message{[YES]}\else \message{[NO]}\fi   % => [YES]
\ifcsname nope\endcsname \message{[YES]}\else \message{[NO]}\fi  % => [NO], twice over
\unlessNegate the conditional that follows: \unless\ifnum 1>2 A\else B\fi runs A. An eTeX primitive. A negated conditional is the same conditional with its arms exchanged, which is how it is lowered.
\unless\ifnum \count0>2 few\else many\fi

Registers

PrimitiveWhat it doesSyntax and example
\countA count register. There are exactly 256 of them (tex.web §236) and they map onto VM slots 0..255, so a read is an array index rather than a hash lookup. texrs loads no format, so every register starts at zero as INITEX leaves them — where the reference tex has plain's values, including \count0 = the page number.
\count<N>=<number>
\count1=7
\message{\the\count1}   % => 7
\advanceAdd to a register. Lowers to GetSlot / LoadInt / Add / SetSlot — native ops the JIT can compile.
\advance\count<N> by <number>
\count1=7 \advance\count1 by 5
\message{\the\count1}   % => 12
\multiplyMultiply a register. Like the other arithmetic, it lowers to native fusevm ops on the register's slot rather than a call.
\multiply\count<N> by <number>
\count1=7 \multiply\count1 by 3   % => 21
\divideDivide a register, truncating toward zero as TeX's does.
\divide\count<N> by <number>
\count1=7 \divide\count1 by 2   % => 3
\chardefDefine a control sequence that IS a number: \chardef\active=13 makes \active usable wherever a number is scanned, which is how plain.tex writes \catcode\~=\active. The code is 0..255 and a wider one is ! Bad character code (N).. The value is fixed when it is defined, so it folds while lowering rather than being read at run time.
\chardef\active=13
\catcode`\~=\active
\countdefGive a count register a name, usable in every position the register itself is: assignment, \advance and \the all reach the same register through either spelling. plain.tex's \pageno is \countdef\pageno=0. The register number is 0..255 and a wider one is ! Bad register code (N)..
\countdef\pageno=0
\pageno=7
\advance\pageno by 1
\mathcodeHow a character is set in math mode, as a 15-bit code: class, family and position. INITEX gives a letter §7100+c, a digit §7000+c and everything else its own code, so \mathcode\A is "7141 and \mathcode\+ is 43. Written and read like \catcode, and restored at the end of a group the same way.
\mathcode`\x="2201
\lccodeA character's lowercase form, which is what \lowercase consults. INITEX sets it for the letters and leaves it 0 for everything else -- a character with no case is not lowercased to a null, it is left alone.
\lccode`\A=`\a
\uccodeThe same for uppercase, consulted by \uppercase. Also 0 for a character with no case.
\uccode`\a=`\A
\sfcodeThe space factor a character leaves behind, which stretches the space after it. INITEX gives every character 1000 except an uppercase letter, which gets 999 -- that is what stops a sentence appearing to end at the full stops in "N.A.S.A.".
\sfcode`\A=999
\delcodeA character's meaning as a delimiter, as a 24-bit code naming a small and a large variant. INITEX gives -1 everywhere, meaning "not a delimiter", except the period, whose code is 0.
\delcode`\(="161361
\mathchardefDefine a control sequence standing for a math code, as \chardef does for a character code. The range runs to "7FFF and a wider value is ! Bad mathchar (N)..
\mathchardef\half="2201
\mathcode`\y=\half
"A hexadecimal constant, and ' an octal one (tex.web \u{a7}445). The hex digits are UPPERCASE: "FF is 255 and "ff is an error -- the opposite of ^^ notation, which takes lowercase. plain.tex writes every \mathcode as a hexadecimal constant.
\count1="FF   % 255
\count2='777  % 511
\dimenA dimension register. A dimension is an integer count of scaled points, 65536 to the printer's point, and a unit is an exact integer ratio to a point (tex.web §458) rather than a float -- which is why 1in is 72.26999pt. The units are pt, in, pc, cm, mm, bp, dd, cc and sp. \the writes one back by Knuth's print_scaled (§103), the fewest digits that read back as the same integer, and \number gives the scaled points instead.
\dimen0=1in
\message{\the\dimen0}   % => 72.26999pt
\dimendefGive a dimension register a name, as \countdef does for a count. The name behaves as the register does on both sides: an assignment through it reads a dimension, and \the through it writes one.
\dimendef\dimen@=0
\dimen@=2pt
\skipA glue register: a natural dimension that can stretch and shrink. The stretch and shrink may be infinite -- fil, fill, filll -- and an infinite component beats any finite one however large, which is what \hfil is made of. \the writes the components back with plus and minus, omitting a zero one, and \number gives the natural component alone.
\skip0=1pt plus 2pt minus 3pt
\skip1=0pt plus 1fil
\skipdefGive a glue register a name, as \countdef and \dimendef do for theirs. An assignment through the name reads a whole glue, not the dimension at the front of one.
\skipdef\skip@=0
\skip@=1pt plus 2pt
\muskipA MATH glue register: the same natural-plus-stretch-minus-shrink a \skip holds, measured in mu. tex.web §455 makes mu the only finite unit a math glue may be written in, and the only place mu is a unit at all — \muskip0=1pt and \dimen0=1mu are both ! Illegal unit of measure. The arithmetic is a point's: a mu is 65536ths, so \advance, \multiply and a group's save and restore are the ones \skip already had. An infinite component is still fil, fill or filll, in either kind of glue.
\muskip0=3mu plus 1mu minus 2mu
\message{[\the\muskip0]}   % => [3.0mu plus 1.0mu minus 2.0mu]
\muskipdefGive a math glue register a name, as \skipdef does for an ordinary one. The name stands for the register everywhere the spelt-out form works, and only where a math glue is wanted: a \muskipdef name is not accepted as the source of a \skip assignment, nor the other way about.
\muskipdef\thinmuskip=0
\thinmuskip=3mu
\toksA token register: a token list stored VERBATIM, since nothing inside the braces expands -- which is the difference between it and a macro, and why \toks0={\x} reads back as \x whatever \x means. \toks1=\toks0 copies one register to another. \the writes the list back by the token-list rule rather than \string's: a control word carries a trailing space however short, a one-character control sequence does not.
\toks0={a\b c}
\message{\the\toks0}   % => a\b c
\toksdefGive a token register a name, as \countdef does for a count. The list is frontend state like a macro body rather than a number in a slot, so the name stands for the register itself.
\toksdef\toks@=0
\toks@={...}
\numexprAn integer expression, closed by an optional \relax: +, -, *, / with ordinary precedence and parentheses. Division ROUNDS, half away from zero, so \numexpr 7/2 is 4 where \divide gives 3 -- the two are different operations and texrs keeps them apart. An eTeX primitive, so the oracle for it is LuaTeX rather than tex; tests/etex.rs holds the comparison.
\count0=\numexpr (2+3)*4\relax   % => 20
\dimexprThe same for dimensions: the operands are lengths and the multiplier and divisor are integers, so \dimexpr 1pt*3 is three points. The arithmetic happens in scaled points, which is the only form a dimension has.
\dimen0=\dimexpr 1pt+2pt\relax   % => 3.0pt
\glueexprA glue expression, the same grammar as \numexpr over glue. Addition is componentwise with TeX's order rule -- an infinite component beats a finite one however large, a higher infinity beats a lower, and only equal orders add -- while * and / scale every component. An eTeX primitive. The operands are glue WRITTEN OUT; a register standing where one is wanted is not read yet.
\skip0=\glueexpr 1pt plus 2pt+3pt plus 4fil\relax
\muexprThe same expression grammar over MATH glue, and the only kind of expression a \muskip assignment accepts — eTeX gives each unit its own primitive so the two cannot be mixed. Its operands are written in mu, and like \glueexpr's they are glue written out rather than registers.
\muskip0=\muexpr 3mu plus 1mu + 2mu plus 2mu\relax   % => 5.0mu plus 3.0mu

Grouping

PrimitiveWhat it doesSyntax and example
{Open a group (category code 1), which scopes the macro table AND the count registers written inside it: the lowered body is wrapped in save/restore for exactly the registers it assigns. It also delimits a macro argument.
{<body>}
\def\v{OUT}
{\def\v{IN}\message{\v}}   % => IN
\message{\v}             % => OUT
}Close a group (category code 2), undoing every non-global assignment made inside it.
{<body>}
\begingroupOpen a group without braces, scoping the macro table and the registers written inside it. It must be closed by \endgroup, not by }.
\begingroup <body>\endgroup
\endgroupClose a \begingroup, undoing every non-global assignment made since it. A } will not close one, and neither will the end of the file.
\begingroup <body>\endgroup
\aftergroupHold the next token and insert it after the enclosing group closes (tex.web §326). It goes on the SAVE STACK rather than in a list of its own, which is what makes several in one group come back in the order they were given and a nested group's tokens come out at ITS } — so a whole call can be assembled a token at a time. Outside every group there is nothing to wait for and the token is inserted at once.
\aftergroup<token>
{\aftergroup\message\aftergroup{\aftergroup X\aftergroup}}   % => X
\afterassignmentHold the next token until the following ASSIGNMENT has finished (tex.web §1269). One only: a second \afterassignment before the assignment replaces the first. A \message is not an assignment and neither is a group, so the token waits for the next \def, \let, register write or arithmetic and is inserted after it.
\afterassignment<token>
\def\m{\message{DONE}}
\afterassignment\m \count1=5   % => DONE, with \count1 already 5

Intercepts

PrimitiveWhat it doesSyntax and example
\interceptRegister advice on macro expansion: before puts the handler's body in front of the expansion, after puts it behind, and around replaces it with \proceed standing for what the macro would have expanded to. The pattern is a GLOB over macro names, so advice registered now catches macros a package defines later. The handler is a macro that takes no parameters. Like \def, this takes effect at compile time and is undone by the group it was registered in.
\intercept{before|after|around}{<glob>}{\handler}
\def\greet#1{HELLO-#1}
\def\trace{[in]}
\intercept{before}{greet}{\trace}
\message{\greet{WORLD}}   % => [in]HELLO-WORLD
\proceedInside an around handler, what the intercepted macro would have expanded to. A handler with no \proceed replaces the call outright, which is how advice suppresses one. Outside an around handler it means nothing.
\def\loud{<<\proceed>>}
\intercept{around}{greet}{\loud}
\message{\greet{WORLD}}   % => <<HELLO-WORLD>>

Inline Rust

PrimitiveWhat it doesSyntax and example
\rustOpen a block of Rust compiled and loaded at run time. The body is Rust, not TeX — it is lifted out of the file BEFORE the mouth reads it, because #, {, } and & are category codes the mouth would act on. Every #[no_mangle] pub extern "C" function the block exports becomes callable with \rustcall. Needs rustc on PATH; the compiled library is cached by body hash, so a second run does not compile it again.
\rust{ <rust source> }
\rust{
    #[no_mangle]
    pub extern "C" fn twice(n: i64) -> i64 { n * 2 }
}
\rustcallCall a function a \rust block exported. The name runs to the first space, the arguments are numbers, and \endrust ends the list. It is a NUMBER wherever TeX reads one — a register assignment, an arithmetic operand, a conditional, or a \message body — and in running text it is called for its effect with the value dropped.
\rustcall <name> <numbers…>\endrust
\count1=21
\message{\rustcall twice \count1 \endrust}   % => 42
\count2=\rustcall add \count1 22 \endrust
\rustcompileWhat a \rust{ … } block becomes: compile and register the block whose base64 body follows, up to \endrust. Written by the desugarer rather than by hand, and carried in a brace-free form so it reads correctly whatever the category codes are where the block appeared.
\rustcompile <base64>\endrust
\endrustTerminate a \rustcompile body or a \rustcall argument list. A control sequence rather than a brace, so neither form depends on a category code the document may not have set yet.
\rustcall twice 21\endrust

Files

PrimitiveWhat it doesSyntax and example
\inputRead another file here, sharing every piece of state with it: a macro it defines is defined afterwards, and a \catcode it sets stays set. The name runs to the first space or end of line (tex.web §537) and .tex is supplied when it carries no extension. texrs searches the working directory and then TEXINPUTS, and does not shell out to kpsewhich, so running a document never depends on a TeX Live installation being present. Fifteen text input levels are allowed, counting the document's own, which is tex's limit and tex's wording when it is passed.
\input macros
\input chapters/one.tex

LaTeX

PrimitiveWhat it doesSyntax and example
\newcommandDefine a macro with n positional parameters: \newcommand{\x}[2]{#1 and #2}. LaTeX writes this as a chain of \ifnum...\def, which cannot run here because lowering emits both arms of a conditional, so texrs dispatches on the argument count natively instead. Two divergences from latex.ltx: redefining an existing name is allowed rather than an error, and the [default] form's default is recorded but not yet substituted at a call that omits the bracket.
\newcommand{\NAME}[ARGC][DEFAULT]{BODY}
\newcommand{\greet}[1]{hello #1}
\message{\greet{world}}   % => hello world
\renewcommandRedefine a macro. Identical to \newcommand here, because texrs does not check whether the name already exists in either direction.
\renewcommand{\NAME}[ARGC]{BODY}
\newcommand{\x}{one}
\renewcommand{\x}{two}
\message{\x}   % => two
\providecommandDefine a macro only if the name is free. An existing definition is kept and the new body is consumed rather than left in the document as text.
\providecommand{\NAME}[ARGC]{BODY}
\newcommand{\x}{first}
\providecommand{\x}{second}
\message{\x}   % => first
\DeclareRobustCommandDefine a macro. Robustness is a property of LaTeX's expansion-in-moving-arguments machinery, which has no counterpart here, so this behaves exactly as \newcommand.
\DeclareRobustCommand{\NAME}[ARGC]{BODY}
\DeclareRobustCommand{\x}{text}
\message{\x}   % => text
\documentclassConsumed, with its arguments, and produces nothing except the page. A class is TeX that builds boxes, and nothing here runs it — the boxes on the page come from this engine's own layout rather than from the class, so the directive is read and dropped — which is what lets the REST of the document be read instead of the run failing at line one. The TYPE SIZE among its options is kept, because it needs no class to honour: [11pt] sets the text at 11pt on the 13.6pt leading size11.clo pairs with it, and --pdf sets the page from that.
\documentclass[OPTIONS]{CLASS}
\documentclass[12pt]{article}
\message{the body still runs}
\usepackageConsumed with its arguments, producing nothing, for the same reason as \documentclass: the package cannot be loaded, and dropping it reads the document minus whatever the package would have drawn. One exception: [margin=...]{geometry} is a page rather than a drawing, and --pdf sets the margins, the measure and the text height from it.
\usepackage[OPTIONS]{PACKAGE}
\usepackage[margin=0.95in]{geometry}
\RequirePackageConsumed with its optional arguments, producing nothing. Same treatment as \usepackage; the difference between them is where LaTeX allows each, which does not matter to an engine that loads neither.
\RequirePackage[OPTIONS]{PACKAGE}
\RequirePackage{amsmath}
\PassOptionsToPackageConsumed with both of its arguments, producing nothing, because the package it would have carried options to is never loaded.
\PassOptionsToPackage{OPTIONS}{PACKAGE}
\PassOptionsToPackage{dvipsnames}{xcolor}
\PassOptionsToClassConsumed with both of its arguments, producing nothing. The class counterpart of \PassOptionsToPackage.
\PassOptionsToClass{OPTIONS}{CLASS}
\PassOptionsToClass{a4paper}{article}
\beginOpen an environment. Most environments are the prelude's business, but a VERBATIM one is the lowerer's: it is caught here, before \begin expands, because expanding is exactly what must not happen to the body. A code listing is full of backslashes that are not control sequences, and reading them as control sequences is why a book of code samples could not be read at all. The body is taken as raw characters up to the matching \end.
\begin{ENVIRONMENT}
\begin{verbatim}
\this is text, not a command
\end{verbatim}
\newpageStart a new page. The prelude defined this to expand to nothing, so a book's title page, copyright page and first chapter ran together as one stream of prose and the page count came out at roughly half what the document asks for. Carried through the text as a form feed, which is what the character means, and split out before words are because Rust counts it as whitespace.
\newpage
first page
\newpage
second page
\clearpageStart a new page, after placing any pending floats. There are no floats here, so it is \newpage. Two breaks in a row are one break: \clearpage straight after \newpage does not leave a blank sheet between them.
\clearpage
\cleardoublepageStart a new page, and in a two-sided document a new RIGHT-hand one. The blank verso a two-sided run would insert is not written, so this is \clearpage here.
\cleardoublepage
\pagebreakBreak the page. \pagebreak[0-4] takes an optional strength, which is advice about how badly the break is wanted rather than a break itself; the break is taken either way, because the document asked for one.
\pagebreak
\pagebreak[4]
\chapterBegin a chapter, on a new page. The prelude defined this as its own argument -- the heading text and nothing else -- so no chapter began a page. \chapter*{...} is the unnumbered form and \chapter[short]{long} carries a running-head title; both start a page and both set the long title. The heading is not yet set in a larger face, and the number is not printed.
\chapter{TITLE}
\chapter*{Preface}
\chapter[Short]{A Longer Title}
\sectionBegin a section: the heading text, with a line of vertical space above it and a line below. The prelude defined this as its own argument, so a heading was simply the first words of the paragraph under it and the page held lines that a real run spends on white space. \section*{...} and \section[short]{long} are read the same way as \chapter's. The number is not printed and the heading is not yet set in a larger face.
\section{TITLE}
\section*{Unnumbered}
\section[Short]{A Longer Title}
\subsectionBegin a subsection. Set exactly as \section is -- its own lines, with vertical space around them -- because nothing here sets a heading in a different size yet, and the depth of a heading is otherwise invisible on the page.
\subsection{TITLE}
\subsection*{Unnumbered}
\subsubsectionBegin a subsubsection, set as \section and \subsection are: its own lines, with a line of vertical space above and below.
\subsubsection{TITLE}
\subsubsection*{Unnumbered}
\tableofcontentsSet a table of contents: one line per heading, with leader dots and the page it starts on. The prelude expanded this to nothing, so every book in the corpus opened straight on its first chapter and was short by the pages a contents takes. The page numbers cannot be known when this is read -- the contents is set before the chapters it lists, and it moves them itself -- so what goes into the text here is a REQUEST, and the typesetter breaks and paginates the document repeatedly until the numbers stop moving, which is what running latex twice does with the .aux file.
\setcounter{tocdepth}{0}
\tableofcontents
\chapter{First}
\setcounterSet a counter. Both arguments are read and nothing is set, as the prelude's stub did, with one exception: tocdepth says how deep \tableofcontents lists, 0 for chapters alone and one more for each step down, and it is read here because a contents two levels deeper lists some hundreds of headings a document never asked for. The class default is 2.
\setcounter{tocdepth}{0}
\setcounter{secnumdepth}{-1}
\endtitlepageEnd a title page, which \end{titlepage} runs. It is \newpage, and then \setcounter{page}{1} unless the class is two-sided (extreport.cls, which every book in the corpus loads): the cover sheet is not one of the document's numbered pages, so every number the contents prints is one less than the sheet it stands on. The command is handed on rather than consumed, because an \end... is also what closes a \centering region and a title page is built out of centred pieces.
\begin{titlepage}
cover
\end{titlepage}
\labelName the place this stands, so that \ref and \pageref can point at it. The prelude read the key and produced nothing, which is right -- a label is a name for a place rather than text -- but nothing recorded the place either, so neither reference could be answered. What goes into the text here is the key, marked, and the typesetter reads back which sectioning unit it stands in and which page it fell on.
\chapter{First}\label{ch:one}
\refSet the number of the sectioning unit the label stands in: 1 for the first chapter, 2.1 for the first section of the second. The prelude expanded this to nothing, so see chapter \ref{ch:one} set as see chapter. The number cannot be known where the reference is written -- the unit may not have been read yet -- so the typesetter resolves it, counting chapters and sections the way the class does. A label the document never declared sets ??, as LaTeX's own \@setref does, because a missing reference an author can see is worth more than a silent gap.
See chapter \ref{ch:one}.
\pagerefSet the page the label fell on, in the document's own numbering. Resolved after the contents is built, because the contents is pages of its own and moves every page after it, and resolved repeatedly until the numbers stop moving -- the digits are text on a line, so one reference can move the page the next one names. A label the document never declared sets ??, and text output, which has no pages, sets ?? for every one of these.
See page \pageref{ch:one}.
\centerStart centring the lines that follow. This is what \begin{center} runs, the way latex.ltx has it: \begin{x} is \csname x\endcsname here, so the environment IS this control sequence. Each line inside is positioned by the width it measures rather than at the left margin. Without it a centred line and the line after it were filled into one flowing line, which is most of why a title page collapsed into the prose under it.
\begin{center}
centred
\end{center}
\endcenterStop centring: what \end{center} runs, and the close of the region \center opened. Lines after it go back to the left margin.
\begin{center}
centred
\end{center}
at the margin
\centeringCentre every line up to the end of the group or the environment that holds it. The switch form of \begin{center}: a title page is regularly one \centering inside a titlepage or a minipage rather than a centred environment. Environments here are a macro pair rather than a group, so the region ends at the \end{...} of whichever one is open -- otherwise a single \centering would centre every remaining page of the book.
{\centering centred}
at the margin
\centerlineCentre exactly its argument, on a line of its own. The box form rather than the switch: what follows it is not centred. The prelude answered it with its argument alone, which set an imprint or a colophon flush left in the middle of the page.
\centerline{TEXT}
\centerline{a colophon}
\tabularOpen a table. \begin{tabular} runs this, the way latex.ltx has it: \begin{x} is \csname x\endcsname here, so the environment IS this control sequence. The optional position and the column specification are read and dropped -- columns are as wide as their content here, not as wide as the specification asks. Inside a table & is a cell boundary and \ ends a row; outside one both mean what they meant before. Without it a table was filled into the prose around it as one paragraph: Name Value alpha 1 beta 2.
\begin{tabular}{ll}
Name & Value \\
alpha & 1 \\
\end{tabular}
\endtabularClose a table: what \end{tabular} runs. It also ends the row in hand, so a last row written without a closing \ is still a row.
\begin{tabular}{ll}
a & b \\
\end{tabular}
\longtableOpen a longtable, which is set exactly as tabular is. This is what pandoc emits for every markdown table, so it is the shape of nearly every table in a generated book. A longtable states its head, then its foot, then its body, and it is set head, body, foot -- see \endhead and \endlastfoot.
\begin{longtable}[]{@{}lr@{}}
\toprule
Name & Value \\
\midrule
\endhead
\bottomrule
\endlastfoot
alpha & 1 \\
\end{longtable}
\endlongtableClose a longtable: what \end{longtable} runs, and the same close \endtabular is.
\begin{longtable}[]{@{}ll@{}}
a & b \\
\end{longtable}
\toprulebooktabs' rule above a table, drawn as a filled rectangle the width of the table at booktabs' own \heavyrulewidth of 0.08em. Written 3,455 times across the corpus and drawn none of them before this, because the prelude answered it with nothing.
\begin{tabular}{ll}
\toprule
Name & Value \\
\bottomrule
\end{tabular}
\midrulebooktabs' rule between a table's head and its body, drawn at \lightrulewidth -- 0.05em, thinner than the two outer rules, which is the whole visual difference between them. An optional width may follow and is read and dropped.
\begin{tabular}{ll}
Name & Value \\
\midrule
alpha & 1 \\
\end{tabular}
\bottomrulebooktabs' rule under a table, drawn at \heavyrulewidth as \toprule is. In a longtable it is written in the foot, before the body, and is set where a reader gets it: under the last row.
\begin{tabular}{ll}
alpha & 1 \\
\bottomrule
\end{tabular}
\hlineThe kernel's own horizontal rule across a table, drawn the way \midrule is. Outside a table it does nothing, which is what it did everywhere before tables were set.
\begin{tabular}{ll}
\hline
a & b \\
\hline
\end{tabular}
\tabularnewlineEnd a table row. The unambiguous spelling of \\ inside a table, for the places where \\ would be read as something else; both end a row here, and both take the * and the optional extra space LaTeX allows after them.
\begin{tabular}{ll}
a & b \tabularnewline
c & d \tabularnewline
\end{tabular}
\endheadEnd longtable's head: everything above it is set at the top of EVERY page the table runs onto, not only the first. Written above the body, which is where it is set on the first page.
\begin{longtable}[]{@{}ll@{}}
\toprule
Name & Value \\
\midrule
\endhead
alpha & 1 \\
\end{longtable}
\endfirstheadEnd longtable's FIRST head, when it differs from the one that repeats. What stands above this is set at the top of the first page, and what stands between it and \endhead at the top of every page after that.
\begin{longtable}[]{@{}ll@{}}
Name & Value \\
\endfirsthead
Name & Value \\
\endhead
alpha & 1 \\
\end{longtable}
\endfootEnd longtable's foot: what stands at the bottom of every page the table runs PAST -- so on no page at all when it fits on one, and never on the last, where \endlastfoot stands instead. Written before the body and set under it.
\begin{longtable}[]{@{}ll@{}}
\endhead
\bottomrule
\endfoot
alpha & 1 \\
\end{longtable}
\endlastfootEnd longtable's LAST foot: what is set once, under the end of the table, where \endfoot stands under every page before it. This is the boundary pandoc writes, with \bottomrule above it, in every markdown table it emits.
\begin{longtable}[]{@{}ll@{}}
\endhead
\bottomrule
\endlastfoot
alpha & 1 \\
\end{longtable}
\itemizeOpen a bulleted list. \begin{itemize} runs this, the way latex.ltx has it: \begin{x} is \csname x\endcsname here, so the environment IS this control sequence. Each \item inside it starts its own line, carries a bullet, and sets 2.5em in from the margin in a measure narrowed by the same amount, so a long item wraps inside the list. A list inside a list indents by that again. Without it a list was filled into the prose around it: an itemize of two items read back as first item second item.
\begin{itemize}
\item first
\item second
\end{itemize}
\enditemizeClose a bulleted list: what \end{itemize} runs. The lines after it go back to the margin, or to the indent of the list this one was nested inside.
\begin{itemize}
\item only
\end{itemize}
back at the margin
\enumerateOpen a numbered list, set exactly as itemize is except for the mark: each item carries its own number in the list, counted from one. Pandoc emits an \enumerate with a \def\labelenumi above it for every ordered markdown list; the label definition is read as an ordinary \def and the numbering here is arabic at every level.
\begin{enumerate}
\item first
\item second
\end{enumerate}
\endenumerateClose a numbered list: what \end{enumerate} runs, and the same close \enditemize is.
\begin{enumerate}
\item only
\end{enumerate}
back at the margin
\descriptionOpen a list of terms. The mark of each item is the term in its own \item[...], set in the bold face and followed by the item's body on the same line. An item with no term has no mark, which is the honest reading of a description item that named nothing.
\begin{description}
\item[term] what it means
\end{description}
\enddescriptionClose a list of terms: what \end{description} runs, and the same close the other two lists have.
\begin{description}
\item[term] meaning
\end{description}
back at the margin
\itemStart a list item: its own line, at the depth of the list holding it, with the mark that list sets -- a bullet, a number, or a bold term. \item[label] gives the item an explicit label, which IS the term in a description list and REPLACES the mark elsewhere; the label is TeX and is expanded, so \item[\texttt{--flag}] sets in the monospace face. Outside any list this is the prelude's own \item, which yields its optional argument and nothing else. 8,683 occurrences across the corpus.
\item[LABEL] <text>
\begin{itemize}
\item plain
\item[!] labelled
\end{itemize}
\definecolorName a colour: \definecolor{neonCyan}{HTML}{05D9E8}. The models read are HTML (six hex digits), rgb (three components in 0..=1), RGB (the same in 0..=255), gray and cmyk. A model that is not one of those defines nothing rather than guessing, because a colour read in the wrong model is wrong on every page it reaches. Documents define their palette once in the preamble and refer to it by name afterwards, so without this every later \color names something unknown and the page comes out black.
\definecolor{NAME}{MODEL}{SPEC}
\definecolor{neonCyan}{HTML}{05D9E8}
\providecolorDefine a colour only if that name is not already defined. Otherwise \definecolor.
\providecolor{NAME}{MODEL}{SPEC}
\providecolor{link}{HTML}{05D9E8}
\colorletGive an existing colour another name. A name nothing has defined yet defines nothing.
\colorlet{NEW}{OLD}
\definecolor{brand}{HTML}{FF2A6D}
\colorlet{heading}{brand}
\colorSwitch the colour of everything that follows, until the group holding it closes -- a switch rather than a wrapper, which is why {\color{red}...} colours only what is inside the braces. Takes a defined name, or a model and a spec: \color[rgb]{1,0,0}. A second \color in one group replaces the first rather than nesting, because TeX has one current colour and not a stack. A name nothing defined leaves the text in the colour it already had.
\color{NAME}
\color[MODEL]{SPEC}
{\color{neonCyan}cyan words}
\textcolorColour exactly one argument: \textcolor{neonCyan}{words}. Takes the same two forms as \color. Unlike \color it puts the previous colour back afterwards, so it never leaks into the text beside it.
\textcolor{NAME}{TEXT}
\textcolor[MODEL]{SPEC}{TEXT}
\textcolor{red}{warning}
\pagecolorPaint the page. Drawn under everything else, which is the only order that leaves the words on top of it -- and it has to be honoured, because a document that sets a dark page also sets light text to go on it, and doing one without the other leaves white on white.
\pagecolor{NAME}
\definecolor{bgPrimary}{HTML}{05050A}
\pagecolor{bgPrimary}
\titleformattitlesec's heading format, applied rather than discarded. Both spellings are read — the starred \titleformat*{\section}{FORMAT} and the plain \titleformat{\chapter}[SHAPE]{FORMAT}{LABEL}{SEP}{BEFORE}, whose optional argument follows a mandatory one. The FORMAT is lowered with the title, so the sizes, colours and family in it reach the heading and end with it; it REPLACES the class default for that level, which is what titlesec does, so a format naming no size leaves the heading at the body size. The shape, label, separator and before-code are titlesec's own layout and are still dropped. A format combining switches composes: {\sffamily\bfseries\Huge} reaches the page as the bold cut of the SANS family at \Huge, because family, series and shape are independent axes and each declaration sets only its own.
\titleformat{COMMAND}[SHAPE]{FORMAT}{LABEL}{SEP}{BEFORE}
\titleformat{\chapter}[hang]{\sffamily\bfseries\Huge}{}{0pt}{}
\titleformat*{\section}{\sffamily\Large}
\pandocboundedSet the argument, scaled down if it would overflow the page. Pandoc wraps every figure it emits in this, and a pandoc document also DEFINES it — as a box, a \Gscale@div against the box's height, and a \scalebox — and the document's definition wins over the preamble's. None of that is on this path, so the figure went into a box that was never set and vanished silently, the caption being outside the wrapper and surviving to look right. It is read here instead of expanded, so the argument is set. The bounding is real: an image with no stated size is brought down to the measure and the text height, keeping its proportions, which is what the wrapper's own comment says it is for. Scaling up never happens, which is also what it does.
\pandocbounded{CONTENT}
\pandocbounded{\includegraphics[keepaspectratio]{diagrams/plot.pdf}}
\includegraphicsPlace an image, and reserve the room it takes. PNG and JPEG are read; the file is embedded once however many pages draw it, with its alpha carried as an /SMask. width= and height= are honoured, in any of TeX's units and as a multiple of \textwidth, \linewidth or \columnwidth; give one and the other follows the file's own proportions, give neither and a pixel is a big point, which is what graphicx does with a file that states no resolution. The figure owns its own lines rather than running into the prose either side, and is centred by its own width inside a center region. A file that cannot be found or read costs the document its picture and not its remaining pages. Other keys — scale, angle, keepaspectratio, trim, clip — are consumed and not acted on, and no format beyond PNG and JPEG is read.
\includegraphics[OPTIONS]{FILE}
\includegraphics[width=0.8\textwidth]{figures/plot.png}
\includegraphics[width=5cm,height=3cm]{cover.jpg}
\setmainfontRecord the document's body typeface, and keep the name rather than dropping it. The PDF backend looks the family up on the system and EMBEDS it when it finds a TrueType-flavoured one, so the page is set in that face and measured with its own widths; failing that it falls back to one of the fourteen faces every reader has, chosen by metrics — Arimo, Liberation Sans and Arial all set at Helvetica's widths, and a name nothing is known about falls to Helvetica too. The DVI backend names .tfm fonts and cannot carry an OpenType one, so it still sets in Computer Modern. An optional bracket on either side of the family is consumed — and read: Path= and UprightFont= name a file the document ships, and BoldFont= and ItalicFont= name the files \bfseries and \itshape are set from. A mandatory argument ending in .ttf, .otf, .ttc or .otc, in any case, names that FILE rather than a family — fontspec's other spelling, resolved against Path= the same way — and an explicit UprightFont=/Extension= still wins where a document writes both.
\setmainfont[OPTIONS]{FAMILY}[OPTIONS]
\setmainfont{Arimo}   % embedded if Arimo is installed, else Helvetica's metrics
\setmainfont{Arimo-VF.ttf}[Path=./.fonts/]   % the file itself, embedded from Path=
\setromanfontThe older fontspec spelling of \setmainfont, and the same thing here: it fills the same slot, so whichever of the two the preamble writes last is the family the PDF backend embeds or maps.
\setromanfont[OPTIONS]{FAMILY}
\setromanfont{Arimo}   % identical in effect to \setmainfont{Arimo}
\setsansfontRecord the document's sans-serif family. The name is read and kept and its bracketed options are consumed, but no backend selects it yet: the PDF page is set in the main family throughout, so this records an intention rather than changing the output. Documented because a preamble writes it and the engine resolves it.
\setsansfont[OPTIONS]{FAMILY}[OPTIONS]
\setsansfont{Arimo}
\setmonofontRecord the document's monospace family, the counterpart of \setsansfont, and the face \ttfamily selects. Its options are read the way \setmainfont's are, so a Path=/UprightFont= naming a file the document ships is what the PDF backend embeds — which is the usual case, since a book carries its monospace face beside itself rather than installing it. The DVI backend names .tfm fonts and still sets in Computer Modern throughout.
\setmonofont[OPTIONS]{FAMILY}[OPTIONS]
\setmonofont{Cousine}
\setmonofont{ShareTechMono}[Path=./.fonts/,Extension=.ttf,UprightFont=ShareTechMono-Regular]
\directluaHand a chunk to an embedded Lua interpreter, and RUN it: PUC-Lua 5.3, the version LuaTeX itself embeds. Whatever the chunk prints comes back as input — \count10=20 a\directlua{tex.print(tex.count[10]+5)}b typesets a25b, the manual's own example. The tex table reaches the engine's real registers (tex.count, tex.dimen, tex.toks, tex.glue, and the get*/set*/is* functions), token reaches the input the chunk stands in front of (token.scan_int, scan_dimen, scan_keyword, scan_string, scan_word, scan_csname, get_macro, set_macro), and texio.write reaches the terminal. A chunk that fails STOPS the run with the Lua error as a TeX error, never silently. What is absent rather than stubbed is everything built on node lists: there is no node library, and tex.skip/tex.getbox refuse instead of inventing a value — tex.getglue is the same glue register as five plain numbers. luaotfload.add_fallback is answered for real: it names the faces a glyph the document's own face lacks is fetched from, and the PDF backend loads those families in order, asks each one's cmap for the character, and draws the first face that has it, carrying only the glyphs the document borrowed. \luadirect is the same primitive under ConTeXt's name; \latelua runs its chunk but contributes no input; \luaescapestring makes a TeX value safe inside a Lua string literal; \luafunction, \luafunctioncall and \luadef call a function stored in lua.get_functions_table().
\directlua{CHUNK}
\count10=20 \directlua{tex.print(tex.count[10]+5)}
\directlua{luaotfload.add_fallback("symfb", {"Arial Unicode MS:mode=base;", "Arial:mode=base;"})}
\ttfamilySet in the monospace face, until the group holding the declaration closes. This is what \texttt{...} is made of — {\ttfamily ...} — and it is honoured here rather than in the text command, because a document that redefines \texttt to colour its inline code writes the declaration in the replacement. The face is the file or family \setmonofont named, embedded when it can be found, Courier when it cannot, and the main face when the document named no monospace family at all.
{\ttfamily fixed pitch}
\texttt{fixed pitch}
\slshapeSet slanted, until the group holding the declaration closes. There is no separate slanted cut here, so it is the italic one — which is what \textsl has always expanded to. Like every other font declaration it sets ONE axis: {\sffamily\slshape} is the slanted cut of the sans family, not a replacement of it.
{\slshape leaning}
\textsl{leaning}
\bfseriesSet in the bold series, until the group holding the declaration closes; \textbf{...} is {\bfseries ...}. It sets the SERIES only and leaves the family and the shape as they stand, so {\sffamily\bfseries} is the bold cut of the sans family rather than a replacement of it. The face is the file BoldFont= named in THAT family's options — the sans family's for a sans context, the mono family's for a mono one — or the bold member of the fourteen carrying that family (Helvetica-Bold, Courier-Bold, Times-Bold) where the document named no file. A variable font whose bold is the upright file at another weight is the SAME file, so it comes out as the upright face: instantiating a weight axis is not something this does.
{\bfseries heavy}
\textbf{heavy}
\itshapeSet in the italic face, until the group holding the declaration closes; \textit{...} and \emph{...} are both {\itshape ...}. The face is the file ItalicFont= named in the main family's options, or the italic member of the fourteen — Helvetica-Oblique, Times-Italic — and the main face where the document supplies neither.
{\itshape stressed}
\emph{stressed}
\rmfamilyBack to the body face, until the group holding the declaration closes. One face is in force at a time here rather than a family, a series and a shape combining, so this replaces whatever the group had selected rather than changing one axis of it.
{\ttfamily code \rmfamily prose}
\sffamilySelect the sans-serif family. \setsansfont is read and kept, but no backend selects that family yet, so this sets in the main face — which is what a document asking for sans in a sans-set book wanted anyway. It is a declaration and runs to the end of its group.
{\sffamily sans}
\normalfontUndo every face declaration in force and set in the body face, to the end of the group. A heading writes it to start from the document's own face rather than from whatever was selected around it.
{\bfseries heavy \normalfont plain}
\makeatletterMake @ a letter (category code 11), so LaTeX's internal names like \@ifnextchar become spellable as single control sequences. This is a catcode change, so it takes effect at COMPILE time, exactly as \catcode\@=11 does.
\makeatletter <internal names> \makeatother
\makeatotherMake @ an ordinary character again (category code 12), closing a \makeatletter region. After it, \@x is the control sequence \ followed by the characters @x rather than one name.
\makeatletter <internal names> \makeatother

INITEX category codes

The table INITEX starts from, which is what texrs starts from: no format is loaded. {, }, $, &, #, ^, _ and ~ get their familiar meanings from plain.tex, not from the engine.

CodeCategoryCharacters INITEX puts here
0escape\
1begin group
2end group
3math shift
4alignment tab
5end of lineline feed, carriage return
6parameter
7superscript
8subscript
9ignoredNUL
10spacespace
11letterAZ, az
12otherevery other code (197 of them)
13active
14comment%
15invalidDEL

Builtin calls

What the VM cannot do natively is a builtin call, emitted by the lowerer and dispatched by id. The numbers are a wire format: the bytecode cache keeps compiled chunks on disk and --aot serializes one into the executable it writes, and both call these BY NUMBER — so renumbering an op does not fail to build, it makes every cached chunk and every already-built binary call the wrong function.

ConstantIdWhat it does
MSG_APPEND4000Append one rendered piece to the message being built.
MSG_FLUSH4001Finish the message being built and record it.
DBG_LINE4002A statement boundary, emitted only under `--dap`. The debug adapter stops here; an ordinary run carries none of these ops.
FFI_COMPILE4003Compile and register a `\rust{ … }` block: one argument, the base64 body.
FFI_CALL4004Call a function a block exported: the name, then its arguments.
TEXT4005Append a run of the document's own text.
ARITH_CHECKED4006`\multiply` / `\divide` under TeX's overflow rule: the old value, the operand, and 0 for multiply or 1 for divide.
MSG_CLOSE4007Close an `\input` file: append `)` to the message already recorded, rather than record one, because tex writes the paren hard against what came before it while the stream is joined with spaces. No arguments.
MSG_DIMEN4008Append a dimension, written as TeX writes one: one argument, the value in scaled points.
MSG_GLUE4009Append a glue, written as TeX writes one: four arguments -- natural, stretch, shrink, and the packed orders.
COLOR_PUSH4010Open a colour: three components follow. 4010 and 4011, not 4006 and 4007: those are ARITH_CHECKED and MSG_CLOSE. Registering a builtin twice silently keeps the LAST one, so the colour ops were compiled, called, and quietly handled by somebody else's function -- the chunk was right and the page had no colour in it.
COLOR_POP4011Close the innermost colour.
ERR_SITE4012Record where the command about to run would be reported from: one argument, `tex.web` §311's context display for that point in the source.
TRANSCRIPT_NOTICE4013§1335's `(see the transcript file for additional information)`, written only if something was reported during the run. No arguments.
MSG_MUGLUE4014Append a MATH glue, written as TeX writes one: the same four arguments `MSG_GLUE` takes, with `mu` for the unit.
SCALE_DIMEN4015`tex.web` §453's `<factor><internal unit>`: the register's value, the factor's integer part, and its fraction in 65536ths. A builtin rather than a run of arithmetic ops because §107's truncation and §460's clamp are the port, and one Rust function is where they stay readable.

The --tiers report

texrs --tiers FILE runs a document and then queries fusevm's own eligibility and cache predicates, so the answer comes from the compiler that would have done the work rather than from an assumption about it.

LineWhat it answers
opsHow many bytecode ops the document lowered to, prologue included.
block-JIT eligibleWhether a region of this chunk is a shape the block tier will take at all. Eligibility is not compilation.
block-JIT compiledWhether the block tier actually compiled one, asked of fusevm's cache rather than assumed from eligibility.
largest eligible regionThe widest run of ops the block tier would take, as start..end with its length, or none.
loopsEvery loop header the lowerer emitted, and whether the tracing tier compiled a trace for it. none means the document has no backward branch, which is the usual reason nothing reaches native code.
block-ineligible opsThe ops that disqualified a region, counted by kind. A pair of CallBuiltins is enough, which is why the smallest document reaches no tier.
reaches native codeThe one line that answers the question the flag was run to ask.

Divergences from tex

Where this engine and real tex disagree, taken from tests/known_gaps.txt — the baseline the differential gate enforces in both directions, so this list can neither grow silently nor go stale.

CaseWhat differs
undefined_cs.texAn undefined control sequence is not an error. tex reports `! Undefined control sequence.' and expands it to nothing; texrs prints its NAME into the message stream and exits 0. Found by the parity fuzzer (`cargo run --bin parity-fuzz`). Fixing it means an error path through the expander that the milestone does not have.
plain_count0.texThe oracle is `tex' with the PLAIN format preloaded, where \count0 is the page number and already holds 1. texrs starts every register at zero, as INITEX does, and loads no format. Not an engine bug -- a startup-state difference, recorded so it cannot be mistaken for one later.
let_char_token.tex`\let\egroup=}' copies a CHARACTER token, and printing one through \message must print the character. texrs prints the name with the trailing space a multi-letter control word gets (tex.web 294's print_cs), so the stream ends `INSIDE\egroup ' where tex ends `INSIDE\egroup'. The meaning-copy half of \let is in parity; this is the char-token half.
def_in_conditional_arm.texA `\def' inside an arm of a RUN-TIME conditional is executed while lowering, and lowering emits both arms because neither is decided yet -- so the else arm's definition overwrites the then arm's and the losing branch wins. tex interprets and runs only the arm it took: it prints `<a>' where texrs prints `[a]'. This is a miscompile rather than a refusal, which is what makes it worth a case: nothing errors, the answer is just wrong. It blocks LaTeX's `\newcommand{\x}[n]{...}', whose argument-count dispatch is exactly a chain of `\ifnum...\def...\else'. Fixing it needs the macro table to be run-time state, or the conditional to be decidable while lowering; neither is a small change.
outer_forbidden_use.textexrs records \outer but does not police it. tex forbids an outer macro inside a group being scanned as text and reports `Forbidden control sequence found while scanning text of \message'; texrs expands it and prints the body. \outer is ONLY an error-detection feature, and every position it governs -- an argument, a text group, skipped conditional text -- is one tex reports and recovers from while texrs stops, so enforcing it cannot reach parity either. Recorded rather than half-enforced: a false positive here would refuse a document that works.
param_brace_illegal_number.tex`\def\greet#{1HELLO-#1}' -- `#{' takes NO numbered parameter (tex.web 476 puts the brace in the parameter text as a delimiter, and the macro has none), so `#1' in the body is illegal. tex reports `! Illegal parameter number in definition of \greet.', recovers by doubling the `#', and prints `1HELLO-##1{WORLD}'; texrs accepts the body and reads `#1' as the delimited argument, printing `1HELLO-{WORLD}'. The recovery is the same error model recorded above, but the DEFINITION is wrong here rather than only the recovery. Found by `#{' being implemented: the input had been kept as fuzz/corpus/lower/crash_param_brace.tex because it PANICKED, and once it stopped panicking tests/fuzz_smoke.rs reported it as a seed that now compiles cleanly. Renamed to fuzz/corpus/lower/param_brace_zero_params.tex.

Invocation

USAGE: texrs [OPTIONS] [FILE[.tex]]... [COMMANDS]
       texrs [OPTIONS] \FIRST-LINE      // the arguments are the input
       texrs [OPTIONS] &FMT ARGS        // with a named format
       texrs                            // no arguments: the prompt

TEX OPTIONS

OptionWhat it does
-interaction=MODEbatchmode, nonstopmode, scrollmode or errorstopmode
-jobname=NAMESet the job name
-output-directory=DIRWrite the output there instead of beside the input
-progname=NAMESet the program name
-fmt=NAMEUse a named format
-iniBe initex
-halt-on-errorStop at the first error
-file-line-error, -no-file-line-errorfile:line:error style messages
-recorderRecord the files read
-8bitWrite 8-bit characters as themselves

RUNNING

OptionWhat it does
--replStart the interactive prompt
--jobs=NCompile N documents at once (default: one per core)
--buildCompile into the bytecode cache and stop, without running
--textPrint the document text, not only the message stream
--dviTypeset to FILE.dvi -- first-fit lines, no hyphenation
--pdfTypeset to FILE.pdf, in the font the document asked for
--no-cacheCompile this run rather than reading the bytecode cache
--aotCompile the document to a standalone native executable

LOOKING INSIDE

OptionWhat it does
--dump-tokensPrint the mouth's token stream and exit
--dump-astPrint the command stream the frontend lowered to, and exit
--disasmPrint the lowered fusevm bytecode and exit
--tiersRun it, then report which fusevm tier took its bytecode

EDITORS

OptionWhat it does
--lspSpeak the Language Server Protocol over stdio
--dapSpeak the Debug Adapter Protocol over stdio

CACHE

OptionWhat it does
--cache-statsSay what the bytecode cache holds and where
--cache-clearDelete it

DOCUMENTS

OptionWhat it does
-X new [DIR]Make one (Texrs.toml + index.tex)
-X initMake one here, named after this directory
-X build [--profile P]Build the document this directory is in
-X watch [--profile P]Rebuild it whenever an input changes
-X showSay what the document is and can produce
-X dump [--profile P]Build to stdout, writing nothing
-X bundle fetch URLDownload a bundle into the cache
-X bundle listSay which bundles have been fetched
-X dvi FILE.dviRead what real tex shipped for a document
-X dvi A.dvi B.dviSay whether two files are the same document
-X bib FILE.bibRead a bibliography database
-X bib FILE.auxSay what a document cites, and what is missing
-X bst FILE.bstRead a bibliography style, and check its names
-X bibtex FILE.auxRun the style: write the .bbl a document reads
-X tfm FILE.tfm [C]Read a font's metrics, or one character's
-X vf FILE.vf [C]Read a virtual font: what it really sets
-X pk FILE.pk [C]Read a packed bitmap font, and draw a character
-X otf FILE.otf [C]Read an OpenType font: its tables and its cmap
-X pfb FILE.pfb [C]Read a Type 1 font: its glyphs and their widths
-X map FILE.map [NAME]Read a font map: what a TeX font name means
-X enc FILE.encRead an encoding: what each code is called
-X itar FILE.tar [NAME]Index a tar bundle, or read one file out of it
-X special TEXTSay what a \special means to a driver
--profile NAMEWhich output to build
--interval MSHow often -X watch looks (default 250)

SYSTEM

OptionWhat it does
-h, --helpPrint this
--versionPrint the version banner

Environment

VariableWhat it does
TEXRS_CACHESet to 0, false or no to turn the bytecode cache off for a run; set to a path to put it somewhere else. A disabled cache is how you work around a cache you suspect.
TEXRS_PARALLELSet to 1 to lex a document on several threads. Off by default: the mouth is 22% of the time on the documents measured, so the win did not pay for the coordination.
TEXRS_PRELEX_STATSSet to anything to print the pre-lexer's hit and miss counts when the run ends.
TEXRS_STATICLIBThe libtexrs.a that --aot links against, when the installed copy is not the one you want.

Links