% 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<Lang>@@$(
%          \DeclareNoLetter $#1{Delimiter}{}			% \NcodeNoLetter<Lang>@@$
%          \DeclareNoLetter [[#1]]{Operand}{Attribute}	% \NcodeNoLetter<Lang>@@[[

\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<Lang>@@[[
%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
}
