Received: from mout.gmx.net (mout.gmx.net [212.227.15.19]) by h1439878.stratoserver.net (8.14.4/8.14.4/Debian-2ubuntu2.1) with ESMTP id t7TABKml025114 for ; Sat, 29 Aug 2015 12:11:21 +0200 Received: from relay2.uni-heidelberg.de ([129.206.210.211]) by mx-ha.gmx.net (mxgmx010) with ESMTPS (Nemesis) id 0MDm4E-1ZV8cG2vYk-00HAmD for ; Sat, 29 Aug 2015 12:11:14 +0200 Received: from listserv.uni-heidelberg.de (listserv.uni-heidelberg.de [129.206.100.94]) by relay2.uni-heidelberg.de (8.13.8/8.13.8) with ESMTP id t7TA8pOH001902 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Sat, 29 Aug 2015 12:08:51 +0200 Received: from listserv.uni-heidelberg.de (listserv.uni-heidelberg.de [127.0.0.1]) by listserv.uni-heidelberg.de (8.13.8/8.13.8) with ESMTP id t7SM14t6026719; Sat, 29 Aug 2015 12:08:51 +0200 Received: by LISTSERV.UNI-HEIDELBERG.DE (LISTSERV-TCP/IP release 16.0) with spool id 12511709 for LATEX-L@LISTSERV.UNI-HEIDELBERG.DE; Sat, 29 Aug 2015 12:08:51 +0200 Received: from relay2.uni-heidelberg.de (relay2.uni-heidelberg.de [129.206.210.211]) by listserv.uni-heidelberg.de (8.13.8/8.13.8) with ESMTP id t7TA8pV0000827 for ; Sat, 29 Aug 2015 12:08:51 +0200 Received: from eggs.gnu.org (eggs.gnu.org [208.118.235.92]) by relay2.uni-heidelberg.de (8.13.8/8.13.8) with ESMTP id t7TA8aMd001816 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NO) for ; Sat, 29 Aug 2015 12:08:39 +0200 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZVd3n-0007Y1-1d for LATEX-L@LISTSERV.UNI-HEIDELBERG.DE; Sat, 29 Aug 2015 06:08:36 -0400 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on eggs.gnu.org X-Spam-Level: X-Spam-Status: No, score=-1.2 required=5.0 tests=ALL_TRUSTED,BAYES_50, RP_MATCHES_RCVD autolearn=disabled version=3.3.2 Received: from fencepost.gnu.org ([208.118.235.10]:48217) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZVd3X-0007Sg-89; Sat, 29 Aug 2015 06:08:15 -0400 Received: from localhost ([127.0.0.1]:33019 helo=lola) by fencepost.gnu.org with esmtp (Exim 4.82) (envelope-from ) id 1ZVd3V-0005qc-QF; Sat, 29 Aug 2015 06:08:14 -0400 Received: by lola (Postfix, from userid 1000) id 3282EDF68C; Sat, 29 Aug 2015 12:08:13 +0200 (CEST) References: <55DBB7F6.1070307@clear.net.nz> <55DABA1D.3050706@morningstar2.co.uk> <55DCFDF7.7080305@clear.net.nz> <55DC05BF.1040505@morningstar2.co.uk> <55DC28E8.9090405@morningstar2.co.uk> <55DDFD0A.4000901@clear.net.nz> <55DDACCB.1070104@morningstar2.co.uk> <55DF523F.2050905@clear.net.nz> <55DF48BE.5030503@residenset.net> <55DF5276.7010500@morningstar2.co.uk> <55DF70B2.1090700@morningstar2.co.uk> <55E09AA8.6070100@residenset.net> <55E176DB.6000109@clear.net.nz> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.0.50 (gnu/linux) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 208.118.235.10 Message-ID: <87bndq2yr6.fsf@fencepost.gnu.org> Date: Sat, 29 Aug 2015 12:08:13 +0200 Reply-To: Mailing list for the LaTeX3 project Sender: Mailing list for the LaTeX3 project From: David Kastrup Subject: Re: Catcode changes To: LATEX-L@LISTSERV.UNI-HEIDELBERG.DE In-Reply-To: <55E176DB.6000109@clear.net.nz> (aparsloe@clear.net.nz's message of "Sat, 29 Aug 2015 21:09:47 +1200") Precedence: list List-Help: , List-Unsubscribe: List-Subscribe: List-Owner: List-Archive: Envelope-To: X-GMX-Antispam: 0 (Mail was not recognized as spam); Detail=V3; X-GMX-Antivirus: 0 (no virus found) X-UI-Filterresults: notjunk:1;V01:K0:kVzAk7W6qoY=:5gonSXI+NPaoP7+jtcu9vbYGM8 RKvUCYZJ0fzywQywVB3+QfopX0yrS5efAoEwsBOaTBiXCY/S4PNgFzmuCOtgfykhVkEutYEYS lgTf9di3RpzWop78TtB0Hm5H5b1s6ln5/GPqonj2tYw7hbJ7qbBJD2B+PcVfMD5RmJ/vOEhP0 KcimQ1/IG92DGPgbsUj7dj8XjZUm9L2/j4LlvckRypDIdf4MiroKydc2z6/JCWn+zc2iFgaTo wkShcGz6tV7VM52GsAj3k72lCDUJLXI53DA70oD/JUU8v/c7i+GD8yG/mSLfTBAXj/MDeixbX avLd8Ei+MuY3/vkgje1DUSXVrAF5wqNuysgmMv8bzBuVISk3TiTwDfsor8h8Tzo1ZcSPG2CK+ Oddi6a/n1eg2efNx3/YQTnc9Ezojl8Tg8LFbbBtP5ejNOlFQmUddVu381f8l9UPVVFXWd4HCl YpKoBwQQPcvIX+LcTS/6dOh0eGhOrOrelwKa97uaRznJprjhbcIKEQPW1WDjsFJK3FCZJVWFA Ivl1LJpWemZ5+R/DBo3RYkPp7HuL3/tjb35mVT0r8jCFt7nct2C9bDtQbJw7ZCa79uflxGypm Ej5ej2fOLI1LxhvVmwjmmDIjr9/40hNayylKEwUhJwT2pTgz6MsVzQVYpx+HZ2z+y9lu83vHI puyHwDl7JQt5oPtFfmbtlTTQjSvxkBm80ju3tJZHQK38sS5vINgkGlq54rit3utQ9V5I1Y2Pl 8MYy9dwUmI1YUc6rAUNMaCv7xFMqdXDdGNLIYL2GNYzOcJdNkriphSmFru0Z+PU0/GD4/SpcR 8gDYzeZvDtB1WYWAYhCzE7W/IM4kHc2+PjblZ8rvoWKydG1Gr2L/+rJllONA+pOfPt5IZeVpA gcqhb0NIFWjpoymbFD8P1GhY2I6MF0QZrsF6TrOZNUW9cFIyk2stDbxWEMFAdI+nn1dQPZFho xCjc9qaLT+zairswrsy4cis9DcadfSBzAObK+e9Co0i3soQd0kC8KSUf0WwOAvJ/de3oHJbaD b39KVX3M3U3eT4ETX8f2ZxF0KLb5FGCb3mrHxP9eGT35DyeXaxzecSiEopKI5hCBIRFrhcJbw Tb+0FJqMRW0GlN2po518/hPHPbkj889tEom6iiN/X9+A2aSQ3lmtylKyjBjW/XGbYVH3TGeQa eFIrv72ILej1sKLAWROI0uKIkJyLr4fruEk9IV/bJ7SMhexrxvhqwxz5qy0qK3/Xsl9eCp1ak V+Kp8fgwwUjvpC0CbR4Idx8h6OEyV4r5dJbfz8sXs5cEEd+dIVafUN/dFHUXFzS30eu6UJolu cBP3+E/1lU29qt1oRB59tZivs8z/XskeY0bjtn3GZw6Tk9WJsfq95rPhtHxv5ML06bP0Kpd/D eZddzLhFH3ECzAoRo/9l+1t6e+yi/joHzKtN2q8UNSVSmnrTb70kdKNn41WF0CHhj2cg2J1oc t4bz9/Gw== X-UI-Loop:V01:Ojt8qV75s94=:ELgNF1V3SBy8RjGhCBAEvwbK/bILuEsHlqjCRcFRDns= X-UI-Out-Filterresults: notjunk:1;V01:K0:UlGOMCZno64=:Cl3bb0u1PMau8Li/keWJ9F c+Np4M3pT+Qxx71vJCJ2Y8pcxjNQFOQu0jgdORPgtFBfuZE5zsQpw13meLfpHY2xWdrOzOO/S 8HC+Jc4xaw1/IU93G+RWpvD+HPb4BL/o6eryKYGvVOGyPg0G0pAaCJb8xXO4pIzBGAS1JDKZe n57/BH8PkbQO0f+pQlt13yLUPRvDogVa2bcyh3gDG/2t4UZV/EceJiMZmJsneDz70spbX/nUc dAfcxzHn5HELN6XUPAIwADb2CPURv2ljEuV3BsqWpwfJFa638ZoJWmMddI/eP6f0ntAjgithd fMhTdl43CmxamwGNCxfKYoLyHyscMczgOtciO3yRJEaVZXJWbda8WcKQ7PpbaxrYiCLyMXsf3 cxjuRRMGwzpwFho39Q2Z7ZnCvYrrKoPbYj8EzQPDPzJlyozRFfT5AuWf5F5AINWi/ng3BW02S YZ6YA+ciPgmfXW5Ctg2ojlsb2w7iOjHs1Tsk0rwmWAp+UsX0y8bj X-Scanned-By: MIMEDefang 2.71 on 85.214.41.38 Status: R X-Status: X-Keywords: X-UID: 7858 --=-=-= Content-Type: text/plain aparsloe writes: > On 29/08/2015 9:26 a.m., Bruno Le Floch wrote: >> Joseph understood your point perfectly well. Xparse takes care of >> matching nested parentheses, and does that expandably too. It would >> be possible for Andrew to abuse xparse to grab his parenthesized >> group, but I am not sure whether I want to advise this or not. It >> would also be possible to simply copy the code, but finding out >> where this is done in xparse's code is non-trivial. I could also >> just provide code that does that, but it would take 30 minutes to >> get completely right and clean (the code needs to carefully preserve >> braces when grabbing delimited arguments, for instance). Using >> \tl_set_rescan:Nnn seems reasonable given the above. > >> Bruno > The suggestion that the documentation note the use of empty setups for > \tl_set_rescan:Nnn seems to have sparked a wider discussion (much of > it beyond me, but interesting as a spectator). The final area where > I've used rescanning is in parsing nested commas for functions of > multiple arguments. l3fp has max and min. It is easy to translate an > expression involving possibly nested \max and \min to a form that l3fp > can digest. All the hard parsing work is conducted out of sight in > l3fp. But there are other functions, like greatest common divisor, > \gcd, which have no corresponding l3fp member. To handle an expression > like \gcd(15,24,33) I equate a comma list to the argument 15,24,33 > then pop the items of the comma list into token list variables which > can be converted to integers and fed to the gcd algorithm. But for a > nested expression like > > \gcd(\max(7+\max(5,2),8),\min(24,29),10+5,\gcd(30,45,6)) > > the argument of the outer \gcd can't be equated to a comma list > without mangling the mathematical syntax. I've treated this by > rescanning ( and ) to group begin and end in the argument token list, > then equating a comma list to the rescanned argument. The individual > items of the comma list are now the correct ones. The token lists > resulting on popping items from the comma list are then rescanned with > an empty setup to get everything back to "normal", converted to > integers and fed to the gcd algorithm. > > To me the way rescanning cuts through the complexity makes it seem (as > they say in medicine of an effective drug) like a magic bullet. At one point of time I wrote some stuff in order to read the Lisp-like PL files written by tfm2pl. To avoid any O(n^2) behavior, the bulk of the reading was done in one large \edef where ( had active character syntax and ) had group closing syntax if I remember correctly. Maybe some of the trickery used in \lispdef and friends might be inspirational, so I'll just append the code here. Mind: this is not production code and I don't think I ever got to using it or anything. But it did accomplish the parsing. Maybe it does not actually help since you _have_ to set up weird catcodes before starting and that may be not compatible with your requirements. --=-=-= Content-Type: text/x-tex Content-Disposition: attachment; filename=forvir.doc % \iffalse Hi Emacs! This is -*-LaTeX-*- source ! \fi \ifx\begin\undefined\else \documentstyle[newdoc]{article}\MakePercentIgnore \fi % \title{The {\tt forvir.tex} virtual font generator} % \author{David Kastrup} % \CodelineIndex % \EnableCrossrefs % \begin{document} % \maketitle % \MakeShortVerb{\"} % \begin{abstract} % This is {\tt forvir.tex}, a foreign language virtual font generation % package. Its purpose is to read in a suitable definition of foreign % language virtual font and ligature requirements, and produce based % on that information a bunch of {\tt vpl}-files from the % corresponding {\tt pl}-files of the original fonts. It must be read % before processing your language definition file. It requires plain % \TeX{} to be preread (though hyphenation patterns and exceptions % need not be loaded) or as a format, and should never produce a % dvi-file. % % The {\tt pl}-files can be derived by the utility {\tt pltotf}, and % the resulting files have to be converted into virtual % fonts and {\tt tfm}-files by~{\tt vptovf}. % % These property list files resemble lisp code, and by proper % redefinition of "\catcode"\/s, \TeX{} is capable of scanning and % writing them at reasonable speed. % \end{abstract} % \tableofcontents % % \section{Implementation} % % We will make use of several special control sequences. For this % reason we make the `"@"' character a~letter. % \begin{macrocode} \catcode`\@=11 % \end{macrocode} % Since this is supposed to be the main file to be loaded, we do not % care to save catcodes. In order to parse the lisp-like expressions % of {\tt pl}-files, we make `"("' start a~control sequence, so that % the first element of a list (which starts immediately after the % opening parenthesis) will be interpreted as a~control word. We % sometimes assemble a~control word by "\csname"\dots"\endcsname" and % generate part of it by~"\string", so `"("' can become part of % a~command name as well. This is because "\string" will produce % "\escapechar" in the first character position, which we set to~`"("' % in order to be able to write, say, `"\noexpand\LIG"' and get % `"(LIG"'. % % Some other times, when we are parsing a~nested expression, we will % make `"("' an opening brace. And sometimes we will have to output % parens as well. % % Since we can easily use real braces and backslashes in our file here % when that functionality is desired, we will make `"("' a~letter % while reading this file. Then we can use it in~"\write", and in % command names. % \begin{macrocode} \catcode`\(=11 % \end{macrocode} % \def\MakePrivateLetters{\makeatletter\catcode`\(11 } % \begin{macro}{\vplfile} % \begin{macro}{\vplwrite} % What else do we need? A file to write our new virtual font % information to: % \begin{macrocode} \newwrite\vplfile \def\vplwrite{\immediate\write\vplfile} % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\font@} % We need a~font to temporarily modify the values of "1ex" and "1em" % depending on the font we are currently working with. We use a text % font guaranteed to be present: % \begin{macrocode} \let\font@=\nullfont % \end{macrocode} % \end{macro} % \subsection{Syntactical definitions} % \begin{macro}{\lispdef} % Most of the time, we will syntactically have to interpret % lisp-like expressions. Those start with an opening brace, the % function name, and then the arguments followed by a~closing % brace. We will want to convert this into a \TeX-like call. % Mostly, an opening brace will be our escape character, and % a~closing one will be an end-group character. We assume this % holds. % % Now the real challenge is that we might have to pass some % parameters which are {\em not\/} included in the lisp-style % argument. These parameters {\em must\/} be single tokens, or the % whole scheme presented here will break. We write, for example, %\begin{verbatim} %\lispdef#1#2\xxx#3{...} %\end{verbatim} % and mean that the macro "\xxx" gets passed two tokens in % \TeX-style, and one in Lisp-style. We need not even specify "#3" % here, the token will be converted to \TeX-style regardless of % whether it is actually included in the argument list. This is % because the conversion really consists only of tacking on % an~opening brace. This also means that the lisp-style argument % gets scanned only when really used, meaning that "\catcode" % changes can take effect when used in~"\lispdef" without % an~argument. % \begin{macro}{\acclispdef} % We use % a~word called "\acclispdef" to accumulate the \TeX~parameters in % its first argument, starting with none. Th second argument is % an~`amendment' explained later. % \begin{macrocode} \def\lispdef{\acclispdef{}{}} % \end{macrocode} % \begin{macro}{\addhash} % \begin{macro}{\endlispdef} % Now our first argument will be the accumulated list, our second % either a~hash mark, or the token we want to define. In the first % case, we call~"\addhash", in the second case~"\endlispdef". % \begin{macrocode} \def\acclispdef#1#2#3{\ifx###3\expandafter\addhash \else \expandafter\endlispdef\fi #3{#1}{#2}} % \end{macrocode} % The first argument here will be the hash mark (I~really wanted to % error-check this by making the first argument "##", but for some % reason, \TeX{} does not allow this inside of parameter % lists). The fourth one will be the corresponding number, so this is % easy. % \begin{macrocode} \def\addhash#1#2#3#4{\acclispdef{#2#1#4}{#3}} % \end{macrocode} % \begin{macro}{\endlispdefii} % "\endlispdef" will just fix up a~proper macro name, and % call~"\endlispdefii". % \begin{macrocode} \def\endlispdef#1{\expandafter\endlispdefii \csname lf@\string#1\endcsname #1} % \end{macrocode} % Now we are getting into {\em very\/} deep water. We want to % define our control sequence to call up the second control % sequence. We want it to take the \TeX{} arguments, and pass them % to our second one. We want to insert an opening brace after the % call to the second control sequence and the \TeX{} argument % tokens, this we can do by an appropriate "\iffalse" around % a~closing brace. It has to be reached by a~sequence % of~"\expandafter"s in order to be expanded before argument % recognition. % % Are we having fun yet? % \begin{macrocode} \def\endlispdefii#1#2#3#4{% \edef#2#3{#4\noexpand\expandafter \noexpand#1% \xpspread#3\end\end \noexpand\expandafter {\noexpand\iffalse}\noexpand\fi}% \def#1#3} % \end{macrocode} % \begin{macro}{\xpspread} % "\xpspread" is supposed to insert appropriate "\expandafter"s % around the argument list, and stops when "\end\end" appears in % its argument list. Including "\end" as a~defined limiter % is an~instance of defensive programming. We could just use no % delimiter, and take the first non-hash token as such, but % undelimited parameters are unbraced, space-skipped, etc. Although % in the current version of "\endlispdefii" this would work, it % would not be so very modification-safe. % % "\xpspread" gets {\em two\/} arguments, because at the time of % the macro call of~"\xpspread" (when executing the~"\edef") the % "#3" will have been replaced as an~"\endlispdefii" parameter with % a~string consisting of hash marks and numbers. Only when those % are contributed to the "\edef" are they converted into the final % parameter form by~\TeX. % \begin{macrocode} \def\xpspread#1#2{\ifx#1\end \else \noexpand\expandafter ###2% \expandafter\xpspread\fi} % \end{macrocode} % Now "###2" will, after argument substitution, become % something like "#1", which the "\edef" will interpret % accordingly. When the definition is called later, this will be % replaced by a~single \TeX-like token, and it will have % an~"\expandafter" tacked on in front of it. % % Imagine what fun I~had in debugging this. Wizards might enjoy % watching these macros in action via~"\tracingall". % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \begin{macro}{\lispdelimdef} % \begin{macro}{\lisptackend} % \begin{macro}{\lisptackendii} % Sometimes we want to parse a~lisp expression which is unwrapped, % without braces, and delimited by~"\end". We do this with % \begin{macrocode} \def\lispdelimdef{\acclispdef{}% {\noexpand\expandafter\noexpand\lisptackend}} % \end{macrocode} % Oh that was the reason for the ``amendment'' above! % \begin{macrocode} \def\lisptackend#1#{\lisptackendii{#1}} \def\lisptackendii#1#2{#1#2\end} % \end{macrocode} % How does this work? The "\noexpand"s are just for the sake of the % "\edef", and the "\expandafter" gets our brace expansion done. % This brace is used as an~argument delimiter for "\lisptackend" % (the tokens before cannot be braces) which has the main task of % wrapping up all up to the opening brace into braces again. % Afterwards, "\lisptackendii" unwraps both bracings, and tacks % the~"\end"~on. % \end{macro} % \end{macro} % \end{macro} % \subsection{Numeric conversions} % In our source we have numbers represented in a special notation: % a~letter followed by a representation of the value. We have % \begin{description} % \item[D] followed by a decimal number. % \item[R] followed by a decimal fraction representing a~fixed % point number with 20~bits of decimals, usually in units % of the design size. % \item[O] followed by an octal number. % \item[H] followed by a~hexadecimal number. % \item[C] followed by a~character. This must be a~`harmless' % character, not a paren or the like. The standard tools % generate this number property only for letters and % digits, which is "\catcode"-safe. % \item[BOUNDARY] followed by nothing can appear in some properties % as a number substitute. We convert this into the % numerical value 256. % \end{description} % We want to have a unique representation of all those characters, % which we can employ just as a number. And in case of real numbers, % we want to preserve the full precision until we know what we will do % with the number. So we will choose a~decimal representation, and % we'll keep the strings in case of the `R' and `D' properties. % % \begin{macro}{\deccode} % \begin{macro}{\deccodeii} % Ok, here we go. "\deccode" expands into a~decimal representation % of its argument. And `expansion' means that this works entirely % in~\TeX's mouth, i.e., in an~"\edef". We echo the tokens without % braces, then set up "\end" as a delimiter at the right of them, % and call "\deccodedelim" which will do the rest. "BOUNDARY" gets % converted to~256, by the~way. % \begin{macrocode} \def\deccode#1{\deccodedelim#1\end} \def\deccodedelim#1{\csname gc@#1\endcsname} % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\numconvert} % "\deccodeii" works by getting the next character, forming a % control sequence with the name "\gc@" followed by the appropriate % letter, and calling that. "gc" was probably an abbreviation for % `get converted', but I forgot. % % Most conversions are done by "\numconvert", which works by % inserting an appropriate character and using the "\number" % primitive. Note how additional spaces before the number are % stripped by using "#2" as undelimited argument in~between. Note % also that the space after "#3" is swallowed by "\number". % \begin{macrocode} \def\numconvert#1 #2#3\end{\number#1#2#3 } \def\gc@O{\numconvert'} \def\gc@H{\numconvert"} \def\gc@C{\numconvert`} % \end{macrocode} % \end{macro} % Decimal and real conversions should just keep the value. We use two % parameters here so that the first one is an undelimited parameter % which will strip any leading spaces. For better syntax checking, % however, we include one literal space as a~requirement. % \begin{macrocode} \expandafter\def\expandafter\gc@D\space #1#2\end{#1#2} \let\gc@R\gc@D % \end{macrocode} % The last possibility is simply `"BOUNDARY"'. This is simple, if not % droll. % \begin{macrocode} \def\gc@B OUNDARY\end{256} % \end{macrocode} % Ok, here is an extension: We want to be able to use an internally % allocated number. We do this with `"N"' followed by the % control sequence name (no escape character). % \begin{macrocode} \expandafter\def\expandafter\gc@N \space #1#2\end{% \number\csname#1#2\endcsname} % \end{macrocode} % \subsection{Printing dimensions} % % \begin{macro}{\aftergroupdimen} % \begin{macro}{\contribute} % "\aftergroupdimen" will convert a dimensional value into a~real % representation, which will be gathered by "\aftergroup", complete % with "R" prefix. Of course, you will have to enclose the call by % "\begingroup \endgroup" and appropriate "\aftergroup"s telling % what to do with the string. % % To put a whole string after the group (delimited % by "\end") we use "\contribute". % \begin{macrocode} \def\contribute#1{% \ifx#1\end \let\next\relax \else \aftergroup#1% \let\next\contribute \fi\next} % \end{macrocode} % Ok, here we go with "\aftergroupdimen". `"R "' is first. % \begin{macrocode} \def\aftergroupdimen#1{% \aftergroup R\expandafter\aftergroup\space % \end{macrocode} % Next we produce the sign, and assign the absolute value of the % dimension to "\dimen@i". % \begin{macrocode} \dimen@i #1\relax \ifdim\dimen@i<\z@ \dimen@i -\dimen@i \aftergroup -% \fi % \end{macrocode} % Ok, now we can output the leading decimal digits. We get this by % putting them into "\dimen@". Oh, by the way, we output our value % relative to the dimension "\unit". % \begin{macrocode} \dimen@\dimen@i\divide\dimen@\unit \expandafter\contribute\number\dimen@\end % \end{macrocode} % Output a decimal point. % \begin{macrocode} \aftergroup.% % \end{macrocode} % Ok, now to the decimal fraction. Since our real variables have % 20~fractional bits, we can safely represent them using 20~digits, % after which additional digits can not possibly have any influence % on the number representation. Of course, this is overkill, but % better safe than sorry. We use "\count@" as our loop variable. % \begin{macrocode} \count@ 20 \loop % \end{macrocode} % First we put into "\dimen@i" the remainder of the last division % which produced the last digit in "\dimen@". % \begin{macrocode} \multiply\dimen@\unit \advance\dimen@i-\dimen@ % \end{macrocode} % In case that the remainder is zero, we stop the procedure by % clearing our counter (so we don't really get 20~digits when they % would all be trivially zero). % \begin{macrocode} \ifdim\dimen@i=\z@ \count@\z@ \fi % \end{macrocode} % Ok, here comes the loop part. We multiply the remainder in % "\dimen@", which is now smaller than "\unit" by 10, and divide to % get the next digit of the expansion. % \begin{macrocode} \ifnum\count@>\z@ \multiply\dimen@i 10 \dimen@ \dimen@i \divide\dimen@ \unit % \end{macrocode} % Now we just contribute it and loop. % \begin{macrocode} \expandafter\aftergroup\number\dimen@ \advance\count@\m@ne \repeat} % \end{macrocode} % \end{macro} % \end{macro} % \subsection{Various property behaviours} % \begin{macro}{\echodef} % \begin{macro}{\echodo} % \begin{macro}{\endecho} % "\echodef"\meta{name} defines the lisp `call' "("\meta{name} % simply as echoing itself along with its argument. This is used % for properties which are passed on without looking at them. % % First we define "\echodef" as defining a control sequence % executing "\echodo" with itself as start of~argument. % \begin{macrocode} \def\echodef#1{\def#1{\echodo#1}} % \end{macrocode} % Then we define "\echodo" to scan in a group, and put the lisp % call triggering it at the head of the token list. In order not to % interpret comments, allow them to have nested parentheses, and be % structured in lines, we have to adjust a~few "\catcode"s. % \begin{macrocode} \lispdef\echodo{\begingroup \catcode`\(\@ne \catcode`\^^M12 \endecho} % \end{macrocode} % And this is ended by calling "\endecho" which will write the % token list together with a closing `")"'. The opening one will % appear automagically because we will later adjust "\escapechar" % to `"("' instead of `"\"'. % \begin{macrocode} \def\endecho#1{\endgroup\vplwrite{\noexpand#1)}} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \begin{macro}{\getdef} % \begin{macro}{\dogetdef} % "\getdef"\meta{name} will not echo, but put the scanned tokens in % the special global variable "\@v("\meta{name}. % % We let this work by calling "\dogetdef" with the name of the control % sequence to be assigned. A~suitable "\gdef" is generated. % \begin{macrocode} \def\getdef#1{\def#1{\dogetdef#1}} \lispdef#1\dogetdef{\expandafter\gdef \csname @v\string#1\endcsname} % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\defgetnumeric} % \begin{macro}{\getnumeric} % "\defgetnumeric"\meta{name} is used to define a~lisp call which % will get and echo a numeric value. The echo will be ``as is'', % but the value will be converted into a decimal representation and % put into "\@v("\meta{name}. % \begin{macrocode} \def\defgetnumeric#1{\def#1{\getnumeric#1}} % \end{macrocode} % We call "\endgetnumeric" with our token and with the string in % question as argument. Again, we will do an open brace by letting % the closing one disappear. % \begin{macrocode} \lispdef#1\getnumeric#2{% \vplwrite{\noexpand#1#2)}% \expandafter\xdef\csname @v\string#1\endcsname {\deccode{#2}}} % \end{macrocode} % The way we did that you can use "\afterassignment" before calling % "\getnumeric", and it will be triggered just after the final % assignment. % \end{macro} % \end{macro} % \begin{macro}{\deffontdimen} % \begin{macro}{\getfontdimen} % We use this to directly enter into the % appropriate fontdimensions of our % scratch font "\font@". The fontdimens will be echoed as well. % % We do this only to properly set up the values of~"1em" % and~"1ex". The other font dimensions are either just echoed or % got by "\getnumeric". % % Syntax is "\deffontdimen"\meta{digit}\meta{name} % % \begin{macrocode} \def\deffontdimen#1#2{\def#2{\getfontdimen#1#2}} % \end{macrocode} % "\getfontdimen" is called with the digit, the name, and the % dimensional value. % \begin{macrocode} \lispdef#1#2\getfontdimen#3{% \vplwrite{\noexpand#2#3)}% \fontdimen#1\font@ \deccode{#3}\unit} % \end{macrocode} % And what value did we assign to our font dimension? Why, the % numeric multiplied by our "\unit", the design size. \TeX{} will % do this multiplication reasonably nice by itself (it truncates % the numeric to 16 fractional bits, but that can be tolerated. The % results of the multiplication are pretty accurate). % \end{macro} % \end{macro} % \begin{macro}{\ignoredo} % Sometimes we want a property to quietly disappear. Such % properties we "\let" to "\ignoredo". % \begin{macrocode} \lispdef\ignoredo#1{} % \end{macrocode} % \end{macro} % \begin{macro}{\enterlist} % \begin{macro}{\cparen} % "\enterlist"\meta{name} will enter the given list (printing its % name), scan it executing its internals, and exit afterwards. If % you want to do more after exiting the list, use "\aftergroup" % after "\enterlist"\meta{name}. % % Most probably you will want a closing paren printed, and % "\aftergroup\cparen" will do the trick. % \begin{macrocode} \def\enterlist#1{\vplwrite{\noexpand#1}\bgroup} \def\cparen{\vplwrite{)}} % \end{macrocode} % Note that the "\bgroup" in "\enterlist" will be matched by the % closing~`")"' of the property. % \end{macro} % \end{macro} % \section{The different Properties} % \begin{macro}{\unit} % \begin{macro}{\DESIGNSIZE} % \begin{macro}{\endDESIGNSIZE} % We store the designsize in the dimension variable "\unit", which % we will first declare. % \begin{macrocode} \newdimen\unit % \end{macrocode} % And of course, "\unit" will be set with % \begin{macrocode} \def\DESIGNSIZE{\afterassignment\endDESIGNSIZE\getnumeric\DESIGNSIZE} \def\endDESIGNSIZE{\unit \@v(DESIGNSIZE\p@} % \end{macrocode} % "\p@" is \TeX's shorthand for "pt", and of course the incredible % "\@v(DESIGNSIZE" contains a~decimal representation of the design % size. % \end{macro} % \end{macro} % \end{macro} % \begin{macro}{\CHECKSUM} % We save the checksum for later reference in our "(MAPFONT" % property. % \begin{macrocode} \getdef\CHECKSUM % \end{macrocode} % \end{macro} % \begin{macro}{\COMMENT} % We simply echo comments. % \begin{macrocode} \echodef\COMMENT % \end{macrocode} % \end{macro} % \begin{macro}{\CODINGSCHEME} % \begin{macro}{\FAMILY} % \begin{macro}{\FACE} % \begin{macro}{\HEADER} % We do the same with a number of other operations of a~more or % less harmless nature. % \begin{macrocode} \echodef\CODINGSCHEME \echodef\FAMILY \echodef\FACE \echodef\HEADER % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \begin{macro}{\SEVENBITSAFEFLAG} % The "(SEVENBITSAFEFLAG" we let disappear silently: we would not % want a~warning just because we utilize upper character codes. % \begin{macrocode} \let\SEVENBITSAFEFLAG=\ignoredo % \end{macrocode} % \end{macro} % \begin{macro}{\BOUNDARYCHAR} % This we simply echo. % \begin{macrocode} \echodef\BOUNDARYCHAR % \end{macrocode} % \end{macro} % \begin{macro}{\CHARACTER} % Just for now\dots % \begin{macrocode} \echodef\CHARACTER % \end{macrocode} % \end{macro} % \begin{macro}{\FONTDIMEN} % Just enter it. % \begin{macrocode} \def\FONTDIMEN{\enterlist\FONTDIMEN\aftergroup\cparen} % \end{macrocode} % \end{macro} % \begin{macrocode} \defgetnumeric\SLANT \deffontdimen5\XHEIGHT \deffontdimen6\QUAD \echodef\SPACE \echodef\STRETCH \echodef\SHRINK \echodef\EXTRASPACE \defgetnumeric\CHARWD \defgetnumeric\CHARHT \defgetnumeric\CHARDP \defgetnumeric\CHARIC % \end{macrocode} % \subsection{Dealing with the Ligature Table} % The ligature table will present us with a~lot of~problems making % it desirable to read it in whole before starting any processing. % In order to do this fast (not having to rescan everything % whenever we enter a new word) we convert this into an internal % representation using just one giant "\edef". Since our input % contains a~lot of closing braces (and we need them to stay % closing braces of some sort, or the "\edef" will never end), we % will have to fake a lot of open ones, since we use `"("' not as % an open brace, but as an escape character. Unfortunately, it is % not much use defining a macro for a fake open brace, because it % will in most cases be easier to fake them directly instead of % wondering how often to expand the macro for it to work. % \begin{macro}{\LIGTABLE} % Ligature tables are processed pretty simply. % \begin{macrocode} \lispdef\LIGTABLE{\vplwrite{\noexpand\LIGTABLE}% % \end{macrocode} % That was almost all needed to be done. Let's go: % \begin{macrocode} \begingroup \afterassignment\endLIGTABLE \edef\ligtable} % \end{macrocode} % After the "\edef" has finished, "\endLIGTABLE" will % be~called, which is really just the start. Note that this "\edef" % will accumulate the whole ligature table. % % A~slight ugliness remains to be told: % The~single properties of the "LIGTABLE" are usually (when % "tftopl" has generated the file) separated by % newlines, which are equivalent to spaces. These spaces will creep % into our ligature table, and there is not much we can do % about~it. We cannot use "\ignorespaces" since it is not % expandable, we cannot use a~macro taking an undelimited argument % (thus stripping spaces) because that macro would fail after the % last property. We {\em could}, however, set the "\catcode" of % newline to `ignore', but if ligature tables were to be formatted % differently, we might get burned if a~newline were to indicate % a~syntactically necessary space. When we are finally executing % our ligature tables, the spaces will be executed, doing nothing % (because we are in vertical mode at the time). We tolerate that. % \end{macro} % Now we define the things we want to have our expressions expand to. % \begin{macro}{\ligtable} % We will read in our ligature table and store it in a~reasonably % compact form as a~macro called~"\ligtable". This macro will % contain entries of the following sort: % \begin{description} % \item["\bslash dolabel"\meta{dec. code}\meta{text code}] is the entry % for a~"(LABEL" directive. The~\meta{dec. code} is the label % code converted into decimal (for a unique representation), % the~\meta{text code} is the label code in its original text % representation which we will employ when writing the ligature % table for better readability. % \item["\bslash dolig"\meta{ligtype}\meta{dec. code}\meta{text code}% % \meta{contents}] represents a~kern or ligature node, where % \meta{ligtype} is a control sequence like "\KRN" or~"\LIG", % the codes are as above, and the \meta{contents} are the % concluding element of the instruction. % \item["\bslash dostop"] represents the "(STOP" instruction. % \item["\bslash doinsert\{"\meta{insertion matter}"\}"] will be put into the % list for material that has to be surrounded by appropriate % "(SKIP" instructions as to not influence the behaviour of % ligature code encountered before. This will be introduced % only by the first pass. % \end{description} % \end{macro} % \begin{macro}{\LABEL} % Labels will be entered as a~macro called "\dolabel" with two % arguments. The first is the decimal representation of the label, % the second one the original of the~source. % \begin{macrocode} \lispdef\LABEL#1{\dolabel{\deccode{#1}}{#1}} % \end{macrocode} % \end{macro} % \begin{macro}{\STOP} % \begin{macro}{\gobble} % We treat "(STOP" next because it is so easy to~do. Almost. We % have to discard the (empty) argument. % \begin{macrocode} \lispdef\STOP#1{\dostop} \def\gobble#1{} % \end{macrocode} % We will certainly need this definition of~"\gobble" a~few more % times. % \end{macro} % \end{macro} % \begin{macro}{\ligdef} % We use "\ligdef" to define all ligature and kern commands. It % will call "\getlig" with a repetition of its~argument. % \begin{macrocode} \def\ligdef#1{\def#1{\getlig#1}} \ligdef\LIG \expandafter\ligdef\csname /LIG\endcsname \expandafter\ligdef\csname /LIG>\endcsname \expandafter\ligdef\csname LIG/\endcsname \expandafter\ligdef\csname LIG/>\endcsname \expandafter\ligdef\csname /LIG/\endcsname \expandafter\ligdef\csname /LIG/>\endcsname \expandafter\ligdef\csname /LIG/>>\endcsname \ligdef\KRN % \end{macrocode} % \end{macro} % \begin{macro}{\getlig} % What will "\getlig" have to do? Take the command code, the % relevant character code (two blank-ended words) and the action % (in case of ligatures, a character code, in case of a~kern, % a~dimension), and form a~proper "\dolig" command with decimal % code, original code and rest. % \begin{macrocode} \lispdelimdef#1\getlig#2 #3#4 #5#6\end{% \dolig#1{\deccode{#2 #3#4}}{#2 #3#4}{#5#6}} % \end{macrocode} % \end{macro} % \subsubsection{The parsing passes on the Ligature Table} % Basically we do a~number of passes on the ligature table in order to % properly work in all the changes to it. All of these passes with the % exception of the output pass work inside of an "\edef", that means % \TeX's mouth. They cannot perform any assignments or do any actual % actions due to that, but only macro expansions. We will see, % however, that this can accomplish a~lot. % % The reason we use only one "\edef" here to build up a whole large % table, instead of adding bit for~bit by separate "\edef"s is that % this would use up time growing quadratically with the size of the % generated list, whereas the single "\edef" uses time only linearly % proportional to the size of the list---a~difference not to be % sneezed~at! % % The output pass does not build any tables any more, so it is pretty % much linear in time as well, although it is not buried in % an~"\edef", does assignments, and calls the counting pass. % % Before further ramblings, here are our passes: % \begin{description} % \item[Reading the table in] % This performs just a~translation into a more or less compact % macro form, and directly uses the ligature table information % from the file as input. `"("' is an escape character, `")"'~is a % closing brace (the latter is necessary to let the closing brace % of the "(LIGTABLE" instruction end our~"\edef"). Due to that, % a~lot of unmatched open braces have to be produced by our % macros. This is taken care of by the "\lispdef"~macros. % \item[Pass~I] % Here we handle `mimicking', where a~new character will behave % like another one when encountered as the right of a~kern % instruction, and `substitutions', where a~label instruction can % be modified by adding additional code or additional labels, % perhaps with own code, and so on. Labels occuring before % insertions are moved behind the insertions, and insertions % itself can again contain mimics or substitutions. % \item[Pass~II] % This is the output pass, which writes the modified ligature % table to the ".vpl"~file. Apart from writing out the separate % commands, it will have to handle insertions by putting skip % commands around them. It intermittently preparses insertions in % counting mode for that reason. Furthermore, all active % substitutions encountered during this pass are disabled for the % remaining substitution pass. % \item[Counting pass] % This simply advances "\stepcount" for every ligature or kern % step encountered. % \item[Remaining substitutions pass] % This does all the substitutions not already done. It has to do % them properly, utilizing both of the main passes in order to get % everything necessary done. % \end{description} % % \begin{macro}{\doperhaps} % Ok, here follow the pass constructs. "\doperhaps" will look at % its argument, and if it is not the same as "\relax" (as all % undefined "\csname"\dots"\endcsname" constructs are) it is % expanded, else it is discarded. % \begin{macrocode} \def\doperhaps#1{\ifx#1\relax\else\expandafter#1\fi} % \end{macrocode} % \end{macro} % % \begin{macro}{\pisetup} % \begin{macro}{\pidef} % \begin{macro}{\pidefii} % \begin{macro}{\pilet} % During pass~I, several macros will have a reassigned behaviour. % We define a pass~I behaviour using "\pidef". It will define % a~macro with the appropriate behaviour, and will put % a~"\let"-assignment of this macro to the original one into the % token register~"\pisetup". % \begin{macrocode} \newtoks\pisetup \pisetup{} \def\pidef#1{% \expandafter\pidefii \expandafter#1\csname pi\string#1\endcsname} \def\pidefii#1#2{% \pisetup\expandafter{\the\pisetup\let#1#2}% \def#2} % \end{macrocode} % "\pilet" can be used, too. % \begin{macrocode} \def\pilet#1=#2{\pisetup\expandafter{\the\pisetup\let#1#2}} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \begin{macro}{\piisetup} % \begin{macro}{\piidef} % \begin{macro}{\piidefii} % This is just as above, only for the output pass (pass~II). % \begin{macrocode} \newtoks\piisetup \piisetup{} \def\piidef#1{% \expandafter\piidefii \expandafter#1\csname pii\string#1\endcsname} \def\piidefii#1#2{% \piisetup\expandafter{\the\piisetup\let#1#2}% \def#2} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \begin{macro}{\cntsetup} % \begin{macro}{\cntdef} % \begin{macro}{\cntdefii} % When inserting material, we sometime have to know how much we % have to insert, in~order to be able to put an appropriate skip % around the inserted material. We do this not really in a~separate % pass, but in piecewise partial passes (this is elaborated later). % % However, all the same technique applies. % \begin{macrocode} \newtoks\cntsetup \cntsetup{} \def\cntdef#1{% \expandafter\cntdefii \expandafter#1\csname cnt\string#1\endcsname} \def\cntdefii#1#2{% \cntsetup\expandafter{\the\cntsetup\let#1#2}% \def#2} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \begin{macro}{\stepcount} % Before we forget it: The counter for this is called "\stepcount". % \begin{macrocode} \newcount\stepcount % \end{macrocode} % \end{macro} % \begin{macro}{\ifskipneeded} % \begin{macro}{\skipneededtrue} % \begin{macro}{\skipneededfalse} % Oh, and another thing we might forget later on: the conditional % indicating whether there have been any ligature or kern steps in % the current program, making it necessary to include a~`"SKIP"' % command when doing insertions. % \begin{macrocode} \newif\ifskipneeded % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \begin{macro}{\dostop} % Now we start to define all this little words embedded into our % ligature table. We start with "\dostop". As long as we are not in % any special passes, we want it to expand to itself, or rather, % not to~expand at~all, like~"\relax". % \begin{macrocode} \let\dostop=\relax % \end{macrocode} % This behaviour needs no change whatsoever in pass~I. % Unfortunately, however, we will have to switch back and forth % between pass~I settings and pass~II settings in one and the same % group, and so every pass~II definition must be offset by one for % pass~I. % \begin{macrocode} \pilet\dostop=\relax % \end{macrocode} % % In pass~II, the output pass, we have a~slight complication: we % want to write a~stop command only if there is any unfinished % ligature program remaining. It~is if the macro "\ifskipneeded", % indicating a~necessary skip if we were to insert any matter, is % true. % % Strictly speaking, this can only happen when we are flushing code % substitutions which were not handled in the normal ligature % table, but that we'll explain later. % \begin{macrocode} \piidef\dostop{\ifskipneeded \vplwrite{(STOP)}% \skipneededfalse \fi} % \end{macrocode} % A~stop command simply does not count in skip distances. So we % do nothing in the count~pass. % \begin{macrocode} \cntdef\dostop{} % \end{macrocode} % \end{macro} % \begin{macro}{\doinsert} % \begin{macro}{\complexinsert} % Now insertions are hairy matter. The way we do substitutes, an % insertion is {\em guaranteed\/} to contain some ligature or kern % step at its end. Thus the label rearrangement business in pass~I % will work properly without intervention even inside of % insertions. % % This makes it possible to let insertions stay passive in pass~I % by~default. Note, however, that the {\em contents\/} of % an~insertion are expanded, thus properly handling mimics and % other~stuff. % \begin{macrocode} \let\doinsert=\relax \pilet\doinsert=\relax % \end{macrocode} % In pass~II, insertions are complicated. When no active code has % been done before (there cannot be any labels without code before % an insertion, because they would have been moved to after the % insertion), the insertion can be simply dropped~in. Otherwise % matters are more complex. % \begin{macrocode} \piidef\doinsert{% \ifskipneeded \expandafter\complexinsert \else \expandafter\unbrace \fi} % \end{macrocode} % Ok, the complex way is to first count the amount of inserted % material, write an appropriate `"SKIP"' command to skip over it, % and then just do the insertion. Unfortunately, we have to skip % not only this insertion, but {\em all\/} up to the next ligature % or kern step. Here we go. The undelimited argument is needed for % unbracing, by~the~way. % \begin{macrocode} \def\complexinsert#1#2\dolig{\begingroup\the\cntsetup \skipcount\z@ #1#2% \vplwrite {\noexpand\SKIP D\number\skipcount)}% \endgroup \skipneededfalse #1#2\dolig} % \end{macrocode} % Of course, when counting through, insertion material is treated % like any other material. To avoid grouping, however, we have to % unbrace it. % \begin{macrocode} \cntdef\doinsert#1{#1} % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\dolig} % We define a~non-expanding behaviour by default for~"\dolig". And % this behaviour will have to extend to its first argument. % \begin{macrocode} \def\dolig{\noexpand\dolig\noexpand} % \end{macrocode} % A~ligature mode needs to be treated special only if it is a~kern. % In that case we check for the presence of "mimic" prefixes which % will cause another character to kern just like the given one. % This needs to know the code to mimic, and if there is no~mimic % code there, we will have to gobble this argument. % \begin{macrocode} \pidef\dolig#1#2#3#4{\ifx#1\KRN \expandafter\doperhaps \csname @m#2\endcsname\gobble{#4}\fi % \end{macrocode} % We have to deal with another case: in order to be able to % properly remove spurious labels in the remaining substitutions % pass, we use the special ligature code "\dolig\end\end\end\end", % which will have to disappear, and be replaced by a~stop command. % \begin{macrocode} \ifx#1\end \dostop \else \noexpand\dolig\noexpand#1{#2}{#3}{#4}\fi} \piidef\dolig#1#2#3#4{% \vplwrite{\noexpand#1#3 #4)}% \skipneededtrue} \cntdef\dolig#1#2#3#4{\advance\stepcount\@ne} % \end{macrocode} % \end{macro} % \begin{macro}{\COPYKRN} % \begin{macro}{\addtomimic} % How to set up such a~"\mimic" behaviour? With "(COPYKRN"\dots")" % which gets two arguments, the first being the code to treat, the % second that it should be likened to. Both will be in the list % number format. First we package this into a complete argument. % \begin{macrocode} \lispdelimdef\COPYKRN #1 #2#3 #4#5\end{% \expandafter\addtomimic \csname m@\deccode{#4#5}\endcsname{#1 #2#3}} % \end{macrocode} % This was just a little preparation of the arguments. Now we~go. % \begin{macrocode} \def\addtomimic#1#2{% \edef#1\gobble##1{\doperhaps#1\gobble{##1}% % \end{macrocode} % Whoa, what will that do? Nothing if "#1" was yet undefined, and % it will echo "#1" with "##1" (that is, the string "#1", not the % argument) at exact the locations we need, if it was already % defined. % \begin{macrocode} \domimic{\deccode{#2}}{#2}{##1}}} % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\domimic} % And what do we have to do with a~mimic call encountered in % pass~I? Easy as pie. Note that this can call further mimics into % action. Note also that the arguments are readily prepared. % \begin{macrocode} \let\domimic=\relax \pidef\domimic{\dolig\KRN} % \end{macrocode} % \end{macro} % \begin{macro}{\dolabel} % And what do we have to do with labels? For labels we might have % substitutes, code to be used instead. And such substitutes must % not come {\em after\/} any labels without intervening ligature ot % kern commands. So what we do is basically the following: if we % have a~substitute, we replace our label by it. If we haven't, we % inactivate further expansion, move our label directly before the % next ligature or kern command, and work on possibly intervening % labels probably to be substituted. % \begin{macrocode} \let\dolabel=\relax \pidef\dolabel#1#2{\expandafter \doperhaps\csname @s#1\endcsname \domovelabel{\noexpand\dolabel{#1}{#2}}} \piidef\dolabel#1#2{% \vplwrite{\noexpand\LABEL #2)}% \expandafter\let\csname @s#1\endcsname \empty} \cntdef\dolabel#1#2{} % \end{macrocode} % \end{macro} % \begin{macro}{\domovelabel} % This will move the label, as told above. The default behaviour of % "\domovelabel" is just to echo its argument (without braces). And % the code for "\domovelabel" on pass~I will % move our label to the front of the next "\dolig". % \begin{macrocode} \def\unbrace#1{#1} \let\domovelabel=\unbrace \pidef\domovelabel#1#2\dolig#3{% #2\ifx#3\end \else#1\fi \dolig#3} % \end{macrocode} % \end{macro} % \begin{macro}{\prefixes} % \begin{macro}{\registerprefix} % This here registers a~possible prefix in "\prefixes", a token % register. It also puts a~label representation into "\next". % \begin{macrocode} \newtoks\prefixes \prefixes{} \def\registerprefix#1#2{% \edef\next{\noexpand\dolabel{\deccode{#2}}{#2}}% \ifx#1\relax \prefixes\expandafter\expandafter\expandafter{% \expandafter\the\expandafter\prefixes \expandafter#1\expandafter\domovelabel \expandafter{\next}\dolig\end\end\end\end}% \fi} % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\COPYLIG} % \begin{macro}{\sameas} % Now a substitute will replace a~label, and be called with the~label % as its argument, wrapped in a~"\domovelabel". One possible % substitute, for example, can be done with % "\SameLigaturesAs"\meta{no1}\meta{no2} which gets two list % numbers as its argument, and places a~label for~\meta{no1} before % that of~\meta{no2}. % % We first fix up the arguments a~bit. % \begin{macrocode} \lispdelimdef\COPYLIG #1 #2#3 #4#5\end{% \expandafter\sameas \csname @s\deccode{#4#5}\endcsname{#1 #2#3}} % \end{macrocode} % This gets increasingly hairy. Remember that an already defined % prefix will dismantle the "\domovelabel", while if none is % defined, at this stage the "\domovelabel" will dismantle itself. % This is all necessary because when the prefix is finally used, it % will really {\em have\/} to dismantle an active "\domovelabel". % % We again use the trick of passing "##1" as first parameter % (literally speaking, it is `"#1"') in order to keep things ready % till we really need the parameter. % \begin{macrocode} \def\sameas#1#2{% \registerprefix#1{#2}% \edef#1\domovelabel##1{% \doperhaps#1% \domovelabel{\next##1}}} % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\AMENDLIG} % \begin{macro}{\sameplus} % If you thought that the last lines were hairy, well here for % something even nicer: "AMENDLIG" gets three arguments, % and the third one is additional ligature code which will be used % together with the ligature code copied from the second argument % as a~ligature table for the first one. % % First, again, argument massaging. % \begin{macrocode} \lispdelimdef\AMENDLIG #1 #2#3 #4#5 #6#7 {% \expandafter\sameplus \csname @s\deccode{#4#5 #6#7}\endcsname{#1 #2#3}} % \end{macrocode} % Well, strictly speaking this is not so much different from the % above, only that we now form an insertion instead of the label. % But I~strongly suggest that you ponder the actions initiated % depending on whether "\doperhaps" finds an expandable macro or % not. And note that the `point of interest', where a~label would % be placed, or where the next substitute command builder will % insert something, moves nicely around with "##1" to magically be % where it is needed. % \begin{macrocode} \def\sameplus#1#2#3#4\end{% \registerprefix#1{#2}% \edef#1\domovelabel##1{% \doperhaps#1% \domovelabel{\doinsert{\next#3#4}##1}}} % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\ADDLIG} % \begin{macro}{\prefprog} % Well, here is the last substitute mangler: a prefix is an % additional piece of ligature (additional to the original one, % that is) which will be executed before the other (original) % steps. The implementation should now appear straightforward (if % you understood the last few chunks). % \begin{macrocode} \lispdelimdef\ADDLIG#1 #2#3 {% \expandafter\prefprog \csname @s\deccode{#1 #2#3}\endcsname{#1 #2#3}} \def\prefprog#1#2#3#4\end{% \registerprefix#1{#2}% \edef#1\domovelabel##1{% \doperhaps#1% \domovelabel{\doinsert{##1#3#4}}}} % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\endLIGTABLE} % Ok, now we are ready for doing the pass~I constructs. Don't worry % about us fouling up the various "\do" commands: they will be % restored on return from the group. % \begin{macrocode} \def\endLIGTABLE{% \the\pisetup \edef\ligtable{\ligtable}% % \end{macrocode} % This was the entire pass~I? A~single simple "\edef"? Well in that % case we better move to pass~II: % \begin{macrocode} \the\piisetup % \end{macrocode} % And now we just go for it: % \begin{macrocode} \ligtable % \end{macrocode} % Ok, now we still might have prefixes not used. We handle them % here: % \begin{macrocode} \the\pisetup \edef\ligtable{\expandafter\empty\the\prefixes} \the\piisetup \ligtable % \end{macrocode} % That should about have rounded up our ligature table. We should % be able to close the matter now. % \begin{macrocode} \cparen\endgroup} % \end{macrocode} % \end{macro} % \subsection{Accents} % Accents are accessed as special control sequences. They % consist of a box, with the width being the horizontal shift % needed for a character of height~0, % the height similar, the depth being the overhang of the accent % (its height minus the xheight of its font). % The second element of an accent is a font selecting control sequence. % If called, it will return a map number, and will allocate one % if necessary. % The third element is "\chardef"ed to the character code of the accent. % \begin{macro}{\newaccent} % These will be temporarily assigned to "\accentbox", "\accentfont" % and "\accentchar". We use "\next" here to temporarily hide the % outerness of~"\newbox". % % \begin{macrocode} \let\@tempa\newbox \let\newbox\relax \lispdef\NEWACCENT#1{% \newbox\next% \expandafter\xdef\csname#1\endcsname {{\number\next}\relax{0}}} \let\newbox\@tempa \let\@tempa\relax \def\get@ccent#1#2#3{% \chardef\accentbox#1% \def\accentfont{#2}% \chardef\accentchar#3\relax} \def\getaccent#1{\get@ccent#1} \def\convertit#1{% \begingroup \font@ \immediate\openout\vplfile=#1.vpl \def\do##1{\catcode`##112 }\dospecials \catcode`\/11 \catcode`\>11 % This is for /LIG> commands and the like \escapechar`( \catcode`\)\tw@ \catcode`\(\z@ \newlinechar`\^^M \catcode`\ 10 % \end{macrocode} % We do not want to have carriage returns to have any effect, so we % change their category to `ignore'. This will {\em still\/} strip % spaces ("\catcode 10") at the start of line. And at the end % of~line, spaces are stripped anyhow at a~very early stage by the % line reading routines of~\TeX{} (long before "\catcode"s % are considered). % \begin{macrocode} \catcode`\^^M10 \vplwrite{\noexpand\VTITLE Accent replacement of #1)}% \input #1.pl \vplwrite{\noexpand\MAPFONT D 0 \noexpand\FONTNAME #1)% \noexpand\FONTCHECKSUM \@v(CHECKSUM ))}% \immediate\closeout\vplfile \endgroup} \catcode`\@=12 \catcode`\(=12 % \end{macrocode} % \end{macro} % \PrintIndex % \end{document} --=-=-= Content-Type: text/plain -- David Kastrup --=-=-=--