diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /info/challenges/aro-bend/answer.008 |
Initial commit
Diffstat (limited to 'info/challenges/aro-bend/answer.008')
-rw-r--r-- | info/challenges/aro-bend/answer.008 | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/info/challenges/aro-bend/answer.008 b/info/challenges/aro-bend/answer.008 new file mode 100644 index 0000000000..b333a5c567 --- /dev/null +++ b/info/challenges/aro-bend/answer.008 @@ -0,0 +1,295 @@ +[The addendum at bottom was not posted with the answer but added in my +archives later ---mjd] + +Date: 22 Jul 1993 15:54:57 -0400 (EDT) +From: Michael Downes <MJD@MATH.AMS.ORG> +Subject: Around the Bend #8 answers +To: info-tex@shsu.edu +X-ListName: TeX-Related Network Discussion List <INFO-TeX@SHSU.edu> + +Exercise 8 asked for a way to trap missing }, \endgroup, or \fi at the +end of a [La]TeX document, in order to give error messages instead +of the warning messages issued by TeX: + + (\end occurred inside a group at level 1) + (\end occurred when \iffalse on line 6 was incomplete) + +This review of solutions is posted later than expected because I +needed time to try out and understand solutions submitted by Peter +Schmitt last week. For clarity's sake, I have split the solutions +into two parts, one dealing with groups, the other with conditionals. + +GROUPS + +Peter Schmitt remarked that if TeX can give a warning message for a +missing endgroup there is nothing to prevent it from giving an error +message except the choice of TeX's author. In some cursory perusal of +TeX: the Program, I wasn't able to find any explanation from Knuth as +to why he didn't make it a real error message instead of just a +warning. Perhaps someone else can shed some light here? + +Now for solutions. The first one was submitted by Peter Schmitt. My +commentary: Assume the body of the TeX document is enclosed within +start and end commands (here named \BEGIN and \END), with the starting +command contributing a \begingroup and the closing command providing +the matching \endgroup, with some juggling to make a group mismatch +trigger an error. + +If the document contains any unclosed groups that were opened with { +or \bgroup, the \endgroup will trigger TeX's low-level error recovery, +which is to insert matching }s (`Missing } inserted'). Thus only the +case of an unmatched \begingroup needs to be handled. Schmitt does +this by (essentially) making a local redefinition of \end that +produces an error message; if all groups are closed properly, the +local definition will disappear, restoring the normal definition, +which will execute a normal endgame. + +Here now Schmitt's submitted solution. I have simplified it slightly +by disentangling some other stuff that will be discussed later below. + +>>Solution 1 (Peter Schmitt) +[a8131dal@awiuni11.edvz.univie.ac.at, schmitt@awirap.bitnet] + +\catcode`_11 + +\let\standard_end\end % save original meaning of end + % define modified end +\def\unexpected_end{% + {\errorcontextlines=0 % minimize errormessage + \errmessage{Unexpected \string\END\space inside group}% errormessage + }\standard_end % continue with \standard_end +} + +\let\End\standard_end + +\def\END{\endgroup\End} + +\def\BEGIN{\begingroup + \let\End\unexpected_end + } + +\BEGIN + +%%% some tests: + +% \bgroup\egroup\end % balanced + \begingroup\end \endgroup % unbalanced +% \bgroup\end % unbalanced +% { \end % unbalanced + +% } \begingroup \end % this is reported +% \endgroup \begingroup \end % this is not reported + +>>EndSolution + +>>Solution 2 (mine) +This solution uses a rather dirty trick with \batchmode. Jonathan +Fine also found the same idea, though in his mail to me he did not +elaborate it into a fully wrapped solution. + +Enclosing the entire document inside a \begingroup \endgroup places an +extra burden on the save stack (one would presume this is why LaTeX's +\begin{document} and \end{document} take some pains to avoid +constructing such a group, although the comments in latex.tex don't +provide an explicit reason). (Extra credit question: Just how much of +a burden would it place on the save stack in, say, an average LaTeX +document?) So my solution seeks to trap unmatched { or \begingroup +without enclosing the document body in a group. The reason the +\batchmode trick is `dirty' is that it leaves a spurious extra error +message in the log file. On screen for the typical interactive user, +this error message is hidden by the temporary switch to \batchmode, +but if for example the user has as part of their TeX system an editor +setup that automatically proceeds through the .log file to help the +user take care of all error messages, then the spurious error message +will be somewhat inconvenient. + +The following clip shows what a user would typically see on screen if +their document contained an unmatched {. + + ! Missing } added. + \bgrouperr ...ffalse {\fi \string } added} + + \enddocument ...rgroup \bgrouperr \egroup + \if \errorstopping \batchmo... + l.50 \enddocument + + ? h + There appears to be an unmatched opening brace or \bgroup somewhere + in your document. + ? + + ) + No pages of output. + +Here then is the code for the solution. As it stands, only the most +recent unmatched open-group is dealt with in the error message. As +the on-screen result from the test section marked as `test 2' will +indicate, a recursive definition for \bgrouperr would be better for +maximum robustness, but I haven't had the spare time to work out the +extra details. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\def\enddocument{% +% Go into \batchmode to suppress possible error messages that we +% don't want to bring to the user's attention. + \batchmode +% Set a flag to enable us to handle the \endgroup properly if the +% \egroup pairs up with an unmatched { or \bgroup. + \def\errorstopping{TF}% +% If the following \egroup matches with a preceding unmatched { or +% \bgroup in the user document, then the aftergroup tokens +% \errorstopmode \bgrouperr will be executed. Otherwise they will +% go away into uncharted limbo. + \aftergroup\errorstopmode\aftergroup\bgrouperr + \egroup +% If there was no unmatched { or \bgroup, then the preceding +% \egroup was discarded by TeX. And \errorstopping is still false. +% Otherwise we need to insert some new \aftergroup tokens. + \if\errorstopping + \batchmode \aftergroup\errorstopmode \aftergroup\begingrouperr + \else + \global\let\bgrouperr\begingrouperr + \fi + \endgroup + \errorstopmode +% Call two different versions of \end, just for convenient testing +% with either plain TeX and LaTeX. + \csname\string @\string @end\endcsname + \end +} + +\def\bgrouperr{% + \def\errorstopping{TT}% + \errhelp{% +There appears to be an unmatched opening brace or \bgroup somewhere^^J% +in your document.}% + \errmessage{Missing \iffalse{\fi\string} added}% +} + +\def\begingrouperr{% + \errhelp{% +There appears to be an unmatched \begingroup somewhere in +your document.}% + \errmessage{Missing \noexpand\endgroup added}% +} + +\newlinechar=`\^^J + +% % Test 0: Leave the following three lines commented out. +%{ % Test 1: uncomment this line +%\bgroup % Test 2: uncomment the previous line and this one. +%\begingroup % Test 3: uncomment all three lines. + +\enddocument + +>>EndSolution + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +CONDITIONALS + +Now, what about \if...\fi matching? Can a method analogous to the one +for groups be applied here? Well, it seems not, since there is no +\afterfi primitive that works like \aftergroup. If you insert an +`extra' \fi it will generate an error message in the case when it is +not needed, and nothing in the case when it is needed; I would have +sworn there's no *detectable* change of state between before the +nonextra \fi and after the nonextra \fi. + +But Peter Schmitt found a scintillating idea, which is to make sure +the \fi is never extra but use the need or non-need of an \else to +control the triggering of an error message. This is done by enclosing +the entire document in a pair of conditions: + + \iftrue\iffalse\else + ... + \fi...\else<error>\fi + +If the \if's and \fi's in the body of the document are properly +matched, then the <error> branch will be skipped over without +execution. But if an unmatched \ifsomething in the document body uses +up the \fi that is supposed to match up with the \iffalse\else, then +the following \else will trigger an error message (which Schmitt hides +with \batchmode, using the same trick as discussed above in Solution +2), then be discarded, and the <error> branch will now be true. + +The extra two conditional structures place no significant burden on +any of TeX's stacks, only a little bit of main memory to keep track of +the line number and type of \if. + +Peter had the group and conditional trapping combined in his original +solution; here is the conditional trapping part as I disentangled it. + +>>Solution 3 (Peter Schmitt): +\catcode`_11 + +\def\fi_message{{\newlinechar`|% % | is used to format screen messages + \errorcontextlines=0 % minimize errormessage + \errhelp{% % help text (if requested by the user) + \END occurred inside a conditional group. |% + You probably have forgotten to close some \fi before. + }% + \errmessage{Unexpected \string\END\space inside conditon} % errormessage + }} + +\def\BEGIN{\def\END{\fi\batchmode\else\errorstopmode\fi_message\fi + \errorstopmode\end}% + \iftrue\iffalse\else + } + +\BEGIN + +%%% some tests: + +% \iftrue \fi \END % balanced + \iftrue \END \fi % error message +% \iffalse \else \END \fi % error message +% \iftrue \iffalse \else \END \fi \fi % warning only +% \iftrue \iffalse \else \fi \END \fi % error message +% \iffalse \else \iffalse \else \END \fi \fi % error message +% \iffalse \else \iffalse \else \END \fi \fi % error message + +>>EndSolution + +In closing, I want to point out that missing \fi's or \endgroup's are +more likely to arise from a TeX programmer's error than from ordinary +use of a macro package like LaTeX. So it might be minimally sufficient +to trap only the missing } case, if the goal is to provide an explicit +error message to end users of such a package. + +Michael Downes + +PS. Hint for Exercise 10: Run the body of the posting through plain TeX. + +ASCII 32--64,65--126: + !"#$%&'()*+,-./0123456789:;<=>?@ +ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Addendum: Found this in comp.text.tex. The line number question is +significant; in Schmitt's solution for handling missing \fi's, you +lose information about the line number where the unmatched \if really +started. + +=========================================================================== +Archive-Date: Wed, 04 Aug 1993 13:30:24 CST +Sender: bed_gdg@SHSU.EDU +From: morje@math.ohio-state.edu (Prabhav Morje) +Reply-To: morje@math.ohio-state.edu (Prabhav Morje) +Subject: "end occurs inside a group" error in LaTeX +Date: 3 Aug 1993 22:36:30 -0400 +Message-ID: <23n7be$e32@math.mps.ohio-state.edu> +Keywords: line number group error +To: tex-news@SHSU.EDU + + Hi, + + I sometimes get the error "\end occured while inside a group +on level 1" while running LaTeX. I know it means there is an extra +"{" somewhere. It is harmless sometimes but if I want to correct it, +LaTeX never tells where the extra "{" is. Is it possible to find the +line number or something more about location of the error? + + Any pointers will be greatly appreciated. + + - Prabhav |