% This is part of the Tcode kernel. Only intended to by input by it. %%%**** Commands for the user, to define the action upon special characters (NoLetter's) ****%%% \def\TcodeDefineSpecial#1{\exP\def\csname NcodeNoLetter\TcodeLang @\string#1\endcsname} \def\TcodeLetSpecial#1{\exP\let\csname NcodeNoLetter\TcodeLang @\string#1\endcsname} %In the next one we do not use \string because the second #1 would fail later anyway. So, #1 should be an 11 or 12 category token. \def\TcodeFormatSpecial#1#2{\exP\def\csname NcodeNoLetter\TcodeLang @#1\endcsname{{#2#1}\@parsecode}} \def\TcodeUnDefineSpecial#1{\exP\let\csname NcodeNoLetter\TcodeLang @\string#1\endcsname\undefined} \def\TcodeUnFormatSpecial#1{\exP\let\csname NcodeNoLetter\TcodeLang @\string#1\endcsname\undefined} %Definitions for comment chars and strings %We place \SetEscapeChars, but don't include it in \CContinueInTheMiddle because \Code makes it "global". %Furhter, \Code, after invoking it, sets it to \relax, to avoid re-applying the same code. % E.g.: \DeclareStringLike{'}{CharLiteral} \def\DeclareStringLike#1#2{\exP\edef\csname NcodeNoLetter\TcodeLang @\string#1\endcsname##1#1{% {\global\let\noexpand\CContinueInTheMiddle\exP\noexpand\csname Format\TcodeLang#2\endcsname \noexpand\CContinueInTheMiddle\noexpand\SetEscapeChars\string#1##1\string#1}\noexpand\@parsecode}} %Here is how the definition would be without \TcodeLang %\def\DeclareString{\exP\def\NcodeNoLetter@"\endcsname##1"{{global\let\CContinueInTheMiddle\FormatString\FormatString "##1"}\@parsecode} \def\DeclareString{\exP\edef\csname NcodeNoLetter\TcodeLang @"\endcsname##1"{{ \global\let\noexpand\CContinueInTheMiddle\exP\noexpand\csname Format\TcodeLang String\endcsname \noexpand\CContinueInTheMiddle\noexpand\SetEscapeChars "##1"}\noexpand\@parsecode}} % E.g.: \DeclareCommentChar{%} \def\DeclareCommentChar{\catcode`\%=12 \catcode`\#=12 \DeclareComment@Char} {\catcode`^^M=13 %How the two middle lines would be without the lang infix (and with \def instead of \edef in the first one): % {\global\let\CContinueInTheMiddle\FormatCommment\FormatCommment\string#1##1}\@parsecode^^M} \gdef\DeclareComment@Char#1{\exP\edef\csname NcodeNoLetter\TcodeLang @\string#1\endcsname##1^^M{ {\global\let\noexpand\CContinueInTheMiddle\exP\noexpand\csname Format\TcodeLang Comment\endcsname \noexpand\CContinueInTheMiddle\string#1##1}\unexpanded{\@parsecode^^M}} \catcode`\#=6 \catcode`\%=14 } } %%% \DeclareNoLetter %%% % E.g.: \DeclareNoLetter $(#1){Delimiter}{Macro} % \NcodeNoLetter@@$( % \DeclareNoLetter $#1{Delimiter}{} % \NcodeNoLetter@@$ % \DeclareNoLetter [[#1]]{Operand}{Attribute} % \NcodeNoLetter@@[[ \newcount\Delim@Variant %It is not necessary to setup the catcodes of the prefix part as they will be within \Code. %We have to take into accout that #1 in the following definitions may be active, ^, etc. %`\{ does need to be changed, otherwise we cannot catch it. \def\DeclareNoLetter{\catcode`\{=12 \Delim@Variant=1 \Declare@Delimited} %Does not invoke the hook \def\DeclareNoLetterP{\catcode`\{=12 \Delim@Variant=2 \Declare@Delimited} %Invokes the hook \def\DeclareNoLetterLong{\catcode`\{=12 \Delim@Variant=3 \Declare@Delimited} %The user parses the code \def\DeclareNoLetterExp{\catcode`\{=12 \Delim@Variant=4 \Declare@Delimited} %The user parses the code \def\Declare@Delimited#1{ \if##\noexpand#1 \errhelp{\string\DeclareNoLetter is used, e.g., as \detokenize{\DeclareNoLetter $(##1){Delimiter}{Macro}}, \detokenize{\DeclareNoLetter [[##1]]{}{Contract}, \DeclareNoLetter ${}{}}. In particular, ## cannot be the first token after \string\DeclareNoLetter.} \errmessage{Wrong use of \string\DeclareNoLetter. The definition will be ignored} \errhelp{} \resetDelimitedCatcodes\exP\DelimitedGrabDefinition \else \toks0=\exP{\string#1} \edef\DelimiterNextCsname{NcodeNoLetter\TcodeLang @@\string#1} \unless\ifcsname Try\DelimiterNextCsname\endcsname \DefineDelimFirstTry#1 \fi \exP\Declare@Delimited@Two \fi } \def\resetDelimitedCatcodes{\catcode`\{=1 \catcode`\}=2 } \def\DelimitedGrabDefinition#1#{\DelimitedGrabDefinition@} \def\DelimitedGrabDefinition@#1#2{} \def\DefineDelimFirstTry#1{ \DefineDelimTry \exP\let\csname NcodeNoLetter\TcodeLang @\string#1\exP\endcsname\csname Try\DelimiterNextCsname\endcsname } %The first time that \DeclareNoLetter is called for a particular combination, say, [[, we define \TryNcodeNoLetter@@[[ %The cs-name to be defined is the contents of \DelimiterNextCsname preceded by "Try" %\string##1 is needed because although the prefix caracters cannot be active, the first non-prefix one may be, and even a control seq. \def\DefineDelimTry{ {\xdef\DD@temp##1{%We do it with {\global ...} becasue the \csname below defines the macro with name the contents of \DelimiterNextCsname, and that cannot be. \noexpand\ifcsname Try\DelimiterNextCsname\noexpand\string##1\endcsname %If adding the next character we still get recognised prefix, proceed. \unexpanded{\exP\exP\csname}Try\DelimiterNextCsname##1\endcsname \noexpand\else %Finish here \noexpand\ifdefined\exP\noexpand\csname\DelimiterNextCsname\endcsname \unexpanded{\def\next}{\exP\noexpand\csname\DelimiterNextCsname\endcsname##1}% putting ##1 back ahead of it \noexpand\else \unexpanded{\def\next}{\the\toks0 \noexpand\@parsecode ##1}%Print the consumed toks0 and let #1 be parsed again \unexpanded{\fi\exP\next} \noexpand\fi }} \exP\let\csname Try\DelimiterNextCsname\endcsname\DD@temp %\exP\show\csname Try\DelimiterNextCsname\endcsname } %In case the user wants "%Print the consumed toks0 and let #1 be parsed again" but applying format to the lone NoLetter. %Defining \DeclareNoLetterLone${Punctuator}, say, will take the branch "% putting ##1 back ahead of it" above, and will to reparse ##1 \def\DeclareNoLetterLone#1#2{ \exP\edef\csname NcodeNoLetter\TcodeLang @@\string#1\endcsname{{\csname Format\TcodeLang#2\endcsname\string#1}\noexpand\@parsecode} } \def\Declare@Delimited@Two#1{%Picking the second character \if##\noexpand#1 \exP\Declare@Delimited@OpeningFinished \else %Second opening character \edef\next{\toks0{\the\toks0 \string#1}}\next \edef\DelimiterNextCsname{\DelimiterNextCsname\string#1} \unless\ifcsname Try\DelimiterNextCsname\endcsname \DefineDelimTry \fi \exP\Declare@Delimited@Three \fi } \def\Declare@Delimited@Three#1{%Picking the third character \if##\noexpand#1 \exP\Declare@Delimited@OpeningFinished \else %Second opening character \edef\next{\toks0{\the\toks0 \string#1}}\next \edef\DelimiterNextCsname{\DelimiterNextCsname\string#1} \unless\ifcsname Try\DelimiterNextCsname\endcsname \DefineDelimTry \fi \exP\Declare@Delimited@Four \fi } \def\Declare@Delimited@Four#1{%Picking the fourth character \if##\noexpand#1 \exP\Declare@Delimited@OpeningFinished \else %We do not allow more than 3 opening characters \errhelp{No more than three characters can appear before ##1. E.g., \string\DeclareNoLetter \string$[[##1]]} \errmessage{Wrong use of \string\DeclareNoLetter. The definition will be ignored} \errhelp{} \resetDelimitedCatcodes\exP\DelimitedGrabDefinition \fi } %Check if the number 1 follows \def\Declare@Delimited@OpeningFinished#1{ \if 1\noexpand#1 \catcode`\{=1 \catcode`\}=12 \exP\Declare@Delimited@ParseClosing \else \errhelp{\string\DeclareNoLetter is used, e.g., as \detokenize{\DeclareNoLetter $(##1){Delimiter}{Macro}} or \detokenize{\DeclareNoLetter [[##1]]{}{Contract}}. In particular, ##1 must follow the opening delimiters.} \errmessage{Wrong use of \string\DeclareNoLetter. The definition will be ignored} \errhelp{} \resetDelimitedCatcodes\exP\DelimitedGrabDefinition \fi } %If the user writes \DelcareDelimited[[#1]]{some-name}{other-name}, the #1 here below will be ]]. \def\Declare@Delimited@ParseClosing#1#{\Declare@Delimited@ClosingParsed{#1}} %#1 may be empty \def\Declare@Delimited@ClosingParsed#1{\catcode`\}=2 \catcode`\^^M=\active \Declare@Delimited@Finish{#1}} %Braces back to normal %#1 is the closing tokens delimiting the argument. #2 the outside format, #3 the inside format. % #2 and #3 may be empty. % % If \Delim@Variant=3, #3 is the code to apply, and includes the tokens #1 within it. % Tex's exasperating duplication of # on replacement makes the definition in this case almost impossible. % This ## illness implies that a token sequence (toks or argument to macro) containing # % only serves to be used in a macro that defines with \def another macro, and nowhere else. % Even \edef\something{\the\toks0 ...}, with the # within toks0, will fail. % %Were not for it, it would be as easy as writing \unexpanded{#3} within the \edef. But on doing that % we get ##1. And wrting {#3} is out of the question because all code in #3 would get expanded. % So, in case 3 the tokens #3 must go directly inside the final \def (this in turn implies that this final \def cannot be an \edef). % % We allow an active ^^M to be the last (typically only) closing character. % \def\Declare@Delimited@Finish#1#2#3{ \catcode`\^^M=5 %#1 already parsed \def\@FormatOutside{#2}\unless\ifx\@FormatOutside\empty \edef\@FormatOutside{\exP\noexpand\csname Format\TcodeLang#2\endcsname} \fi % The opening characters: \toks0 \edef\temp{\toks1{{\expandonce\@FormatOutside\the\toks0}}}\temp % The delimited (or not) argument: ##1 \ifnum\Delim@Variant<3 \def\FormatToApply{#3}\unless\ifx\FormatToApply\empty \edef\FormatToApply{\exP\noexpand\csname Format\TcodeLang#3\endcsname} \fi \edef\temp{\toks1{\the\toks1 \ifnum\Delim@Variant=1 \bgroup\expandonce\FormatToApply\relax####1\egroup%The relax here and below is to cancel a possible \ignorespaces, \else \def\noexpand\FormatToApply{\expandonce\FormatToApply} \WordToPrint{####1}\bgroup\noexpand\PrePrintHook\expandonce\FormatToApply\relax####1\egroup%that would skip the spaces in ##1 \fi }} \temp \else \toks1\exP{\the\toks1{#3}} %To understand why we use here and above\broup \egroup instead of { }, look for it in the Tcode Rationale. \fi % The closing characters: #1 \let\maybe@EOL\empty %Must expand to empty (not \relax) \def\temp{#1} \let^^M\empty \edef\tempM{#1} \unless\ifx\temp\tempM %They are different, hence ^^M was present \let\temp\tempM \def^^M{\noexpand^^M} %Strip ^^M from \temp... . ^^M defined so that it remains in place in the #1 below. \let\maybe@EOL=^^M %... and place it after \@parsecode \fi \unless\ifx\temp\empty \edef\temp{{\expandonce\@FormatOutside\temp}}%There should be nothing expandable in #1 other than^^M \toks1 \exP\exP\exP{\exP\the\exP\toks\exP1\temp} \fi % \@parsecode and maybe a closing ^^M \edef\temp{\toks1{\the\toks1 \noexpand\@parsecode \maybe@EOL}} \temp %\showthe\toks1 % \edef\tobedefined{\exP\noexpand\csname\DelimiterNextCsname\endcsname####1#1} %Idem. \exP\exP\exP\def\exP\tobedefined\exP{\the\toks1} %\exP\show\csname\DelimiterNextCsname\endcsname }