engine: a file is an input LEVEL, and \begin{env} is a group - #104
Conversation
bd4cbe3 to
51c724f
Compare
|
Rebased on
Better than before (16 up/14 down became 18 up/10 down, and one talk still goes 0 → 10 pages), but still not a net win on beamer. Two distinct remainders now, both isolated: 1. Five arXiv papers collapse to almost nothing — 2406.07418 renders 55 232 glyphs on \author{Name\\ Affiliation\\ \And \textbf{Name}\thanks{Corresponding Author}\\
% examples of more authors
% \And
…
}with 2. Ten talks lose pages — the largest 02369dc5 (33 → 17 pages, 11 073 → 5 072 glyphs) and 031d6afb (27 → 11). Not yet diagnosed. Both are the same shape as everything else this branch has turned up: material that used to arrive somewhere harmless now arrives where it belongs, and something downstream cannot take it. Staying a draft until they are. |
51c724f to
bb8ff8a
Compare
|
Chased the first remainder to the bottom and it does not yield here — recording it so nobody pays for it twice. The five arXiv collapses are one defect, and it is reproducible in four lines. NeurIPS's style splits the author block with \def\And{\end{tabular}\hfil\linebreak[0]\hfil\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces}used inside \documentclass{article}
\usepackage[preprint]{neurips_2024}
\title{T}\author{A \And B}
\begin{document}\maketitle CORPSVISIBLE\end{document}
This engine builds tabular in Go, so by the time the cell holding Two guards were implemented and measured, both repair arXiv and both wreck beamer:
−140 pages either way, over 32 talks. The reason is now in the code: beamer's frame IS a box ( So the real fix is upstream of both: the Go-side tabular has to model a table that is split by Branch is back to the unguarded state (the better of the three), rebased, suite green. |
bb8ff8a to
0191091
Compare
|
#106 fixed on this branch, and it moves the needle a long way. TeX's alignment scanner expands as it looks for Re-measured against
The five collapsed papers are back: 2406.07418 renders 55 253 glyphs against What is left is beamer's frame box. Instrumented on 02369dc5 (33 → 17 pages): That is the last thread. It is also the same shape as #106 — a piece of beamer's own bookkeeping that this engine short-circuits — and it is where I would look next: why the frame box's |
|
Counted the group opens and closes per environment on a reduced case (two consecutive frames of 02369dc5, 8 pages against
\global\setbox\beamer@framebox=\vbox\bgroup … \begin{beamer@framepauses} …
\beamer@checkframetitle}% <- fin du code de DEBUT de l'environnement
{\par \end{beamer@framepauses}% <- code de FIN
\egroup … \box\beamer@framebox}so the order beamer relies on is That is the whole of the remaining beamer regression: 10 talks, −43 pages, one cause. It is beamer's frame-box bookkeeping meeting an engine that now has a group where it did not before, and it wants its own pass — the frame box has to be opened and closed by the same discipline arXiv is unaffected by it (+108 pages, 22 up, 5 down), so the branch is one bounded piece of work away. |
|
Traced one level deeper: the frame box is a symptom, not the cause. Printing the group stack at each At the first
\long\def\beamer@@altenv#1#2#3#4#5{\alt#1{\long\def\beamer@eoenv{#3}#2}{\long\def\beamer@eoenv{#5}#4}}
\protected\def\endaltenv{\beamer@eoenv}So the environment's closing code is a macro DEFINED by its opening code, and when that definition does not survive to That is the next piece, and it is beamer's overlay layer rather than anything in this branch: make Branch unchanged (suite green, arXiv +108, beamer −43); this is diagnosis, recorded so the next pass starts here. |
|
Reduced the beamer remainder to four lines. This document: \documentclass{beamer}
\begin{document}
\begin{frame}
\begin{itemize}\item<2-> Un\item<3-> Deux\end{itemize}
\end{frame}
\end{document}renders 3 pages on both One overlay item per slide is enough — no theme, no fragile, no That is the whole beamer regression on this branch, in a document small enough to step through: 2 618 group events, 6 unmatched, all in the last 250. It is where the next pass starts. |
|
Found what leaks in the four-line case, and measured the obvious fix — it is a dead end. Recording both. What leaks. Tracing every environment open/close, the first the second is not: beamer closes an item's three overlay wrappers when the NEXT The fix that suggests itself, measured. Make
−265 pages and half a million glyphs on arXiv. Unwinding closes too much: an environment legitimately still open inside another goes with it. Reverted; the branch stays at So the rule is neither "close the innermost" (leaves beamer's wrappers behind) nor "unwind to my own" (takes honest nesting with it). What LaTeX actually has is |
Two things this engine did not model, both straight from the reference, and
both reached through the same document: a beamer talk with a [fragile] frame.
## A file is a level of input (tex.web §537)
start_input does begin_file_reading — "set up cur_file and new level of input" —
and the file is read to its end before the level underneath resumes; §329
end_file_reading pops back.
This mouth merged a file into ONE character buffer at the mouth's position, and
it reads pending token lists before that buffer, so a file \input from inside a
macro arrived AFTER the rest of that macro's body. It is not a corner case:
\frame<*>[…][{…,fragile=false}]{\begingroup\input{\jobname.vrb}\endgroup}
is how beamer re-reads a fragile frame's body, and the body arrived after the
frame had closed — into a box nothing places.
pushInputLevel/popInputLevel make the file a level of its own, carrying the
buffer, the mouth's position, the line table, the pending lists and the
no-progress guard's baseline (which watches e.bpos, and a new buffer starts at 0
again). \input, \InputIfFileExists, the .bbl reader and class/package loading all
go through it, so the line-number bookkeeping that discounted a loaded file's
lines from the document's own (inputNL/loadedNL/nlCount) is gone: a file no
longer shifts the document's lines because it is no longer in its buffer.
\InputIfFileExists now runs its then-code BEFORE the file, which is what
ltfiles.dtx does: \IfFileExists{#1}{#2\@addtofilelist{#1}\@@input\@FilEf@und}.
## \begin{env} … \end{env} is a group (ltmiscen.dtx)
\protected\def\begin#1{… \begingroup\@endpefalse\reserved@a}
where \reserved@a is \def\@Currenvir{#1}… \csname #1\endcsname
\def\end#1{\csname end#1\endcsname\@Checkend{#1}\expandafter\endgroup …}
\begingroup comes first and \@Currenvir is defined inside it. Without the group
\@Currenvir was never restored, so a class that leaves an environment early by
closing its group could not — and beamer's fragile frame does exactly that:
\def\beamer@checkforfragile#1fragile#2\relax{… \endgroup% end environment
\expandafter\beamer@framecommand\beamer@frameoptions\bgroup}
then calls \frame, which picks its syntax with \ifx\@Currenvir\beamer@frametext.
With \@Currenvir still "frame" the command form took the ENVIRONMENT path a
second time and \beamer@doseveralframes was handed the bare \bgroup instead of
the frame's body — measured, the frame went on the page empty.
The group is opened and closed by the Go-side probes \gotex@checkenv and
\gotex@endenv, not by \begingroup/\endgroup tokens, because \begin and \end must
stay expandable here: as tokens they would print as literal text wherever \begin
is only expanded (inside \message or \edef).
Four consequences, each from the reference:
- Allocation is GLOBAL. ltplain.dtx's \e@alloc ends with
`\global#2#6\allocationnumber`; ltcounts.dtx's \@definecounter makes \cl@<c>,
\p@<c> and \the<c> global; ltthm.dtx's \@xnthm/\@ynthm use \xdef and
\global\@namedef. So \newcount, \newdimen, \newskip, \newlength, \newcounter
and \newtheorem now define globally — otherwise a counter declared inside
\begin{document} (which is an environment) vanished with it.
- Every alignment entry is a group (tex.web §791: a template's u-part and
v-part are inserted inside braces), so a font switch in a cell stops there.
buildCellHList terminated the cell with a bare } sentinel and no group; that
stray brace was silently swallowed until a \begingroup was open.
- An environment the engine implements in Go swallows its own \end, so \end —
and the \endgroup with it — never runs: tabular, tabularx, verbatim,
lstlisting, equation, align, minipage and the gobbled comment environments
each close the group themselves.
- setSrcPos no longer discounts loaded lines: a level reports its own file's
line, which is where a diagnostic about that file belongs.
## Measured
200 real beamer talks and 200 arXiv papers, against main:
beamer 1121 → 1031 pages 182 743 → 171 499 glyphs 16 up, 14 down
arXiv 4052 → 4057 pages 9 485 942 → 9 234 865 14 up, 7 down
NOT a net win yet, and the reason is a defect that predates this branch and is
reproducible on main: with an outer theme of the split/infolines family
(\usetheme{Berlin}, Madrid, Warsaw, Copenhagen) the verbatim capture of a
fragile frame's FIRST line loses everything after its first control sequence —
\textbf{gras} is written to the .vrb as \textbf. Reading that back where it
belongs (inside the frame) then swallows the next line, which is why correcting
the ORDER makes those talks worse. See the issue.
0191091 to
bf3a93e
Compare
|
Found the missing link, and it is one line of the kernel. beamer closes an \def\endtrivlist{%
\beamer@closeitem
… restore …
\beamer@origendtrivlist}and \def\endlist{\global\advance\@listdepth\m@ne \endtrivlist}In this engine The four-line case goes from 6 groups open to 0, and over the corpora:
beamer was −43 pages with 10 talks down; it is now −10 with 8, while drawing 7 974 more glyphs. Both corpora now gain content. Pushed as |
|
Measured against the FOREIGN JUDGE — the 16 talks with a tectonic PDF beside them, so there is a ground truth to be right or wrong about:
No talk moves away from the reference. Two move closer, and one of them (talk 9) goes from rendering 0 pages to exactly the 10 tectonic produces. So the −10 pages over the 200-talk corpus is not a regression, it is convergence: That is the bar I was holding out for, and it is met:
Taking it out of draft. |
Two things this engine did not model, both from the reference, both reached through the same document: a beamer talk with a
[fragile]frame.Draft on purpose. The whole suite is green and every piece is backed by the reference, but measured over 200 real talks it is NOT yet a net win — it is blocked on #103, a defect that predates this branch and reproduces on
main. Details at the end.A file is a level of input (tex.web §537)
start_inputdoesbegin_file_reading— "set up cur_file and new level of input" — and the file is read to its end before the level underneath resumes; §329end_file_readingpops back.This mouth merged a file into ONE character buffer at the mouth's position, and it reads pending token lists BEFORE that buffer, so a file
\inputfrom inside a macro arrived after the rest of that macro's body:That is how beamer re-reads a fragile frame's body, and the body arrived after the frame had closed — into a box nothing places.
pushInputLevel/popInputLevelmake the file a level of its own, carrying the buffer, the mouth's position, the line table, the pending lists and the no-progress guard's baseline (it watchese.bpos, and a new buffer starts at 0 again — without that, every file after the first looked like a loop making no headway).\input,\InputIfFileExists, the.bblreader and class/package loading all go through it, so the bookkeeping that discounted a loaded file's lines from the document's own (inputNL/loadedNL/nlCount) is gone: a file no longer shifts the document's lines because it is no longer in its buffer.\InputIfFileExistsnow runs its then-code BEFORE the file, as ltfiles.dtx does:\IfFileExists{#1}{#2\@addtofilelist{#1}\@@input\@filef@und}.\begin{env}…\end{env}is a group (ltmiscen.dtx)\begingroupcomes first and\@currenviris defined inside it. Without the group\@currenvirwas never restored, so a class that leaves an environment early by closing its group could not — and beamer's fragile frame does exactly that (\endgroup% end environment), then calls\frame, which picks its syntax with\ifx\@currenvir\beamer@frametext. With\@currenvirstillframe, the command form took the ENVIRONMENT path a second time and\beamer@doseveralframeswas handed the bare\bgroupinstead of the frame's body — measured, the frame went on the page empty.The group is opened and closed by the Go-side probes
\gotex@checkenvand\gotex@endenv, not by\begingroup/\endgrouptokens, because\begin/\endmust stay expandable here: as tokens they print as literal text wherever\beginis only expanded (inside\messageor\edef).Four consequences, each from the reference:
\e@allocends with\global#2#6\allocationnumber; ltcounts.dtx's\@definecountermakes\cl@<c>,\p@<c>and\the<c>global; ltthm.dtx's\@xnthm/\@ynthmuse\xdefand\global\@namedef. Otherwise a counter declared inside\begin{document}— an environment — vanished with it.buildCellHListterminated a cell with a bare}sentinel and no group; that stray brace was silently swallowed until a\begingroupwas open.\end, so\endnever runs — tabular, tabularx, verbatim, lstlisting, equation, align, minipage and the gobbled comment environments.setSrcPosno longer discounts loaded lines: a level reports its own file's line, which is where a diagnostic about that file belongs.Tests
12 new tests: the level order (a file read before the rest of the macro that asked for it, what a file leaves pending, nested files returning through each level, a long file after a long one not mistaken for a loop,
\InputIfFileExists's then-code first), and the environment group (a definition ending with its environment,\@currenvirfollowing the nesting,\endgroupleaving the environment, allocation surviving it, alignment cells, and every Go-side environment leaving no group open).Measured — why this is a draft
200 real beamer talks and 200 arXiv papers, against main:
The talks that lose are the fragile ones with a split/infolines outer theme, and the cause is #103: with those themes the verbatim capture of a fragile frame's FIRST line already loses everything after its first control sequence (
\textbf{gras}is written to the.vrbas\textbf) — onmain, before this branch. Correcting the ORDER puts that corrupt line back INSIDE the frame, where it swallows the next one; with the wrong order it landed outside and did less damage.So #103 first, then this. Individually verifiable meanwhile: a fragile frame with no theme now renders its body (it produced an empty page before), and one talk in the 200 goes from 0 pages to 10.