Created
October 9, 2013 01:30
-
-
Save nelhage/6894729 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
%%%%%%%%%%%%%%%%%%%%%%%%% datatypes.sty %%%%%%%%%%%%%%%%%%%%%%%%%% | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
%% | |
%% Commands Provided: | |
%% | |
%% \DECLARETYPE{Foo} | |
%% \DECLARESUBTYPE{Foobar}{Foo} | |
%% | |
%% \PRESETS{Foobar}{STUFF} | |
%% \POSTSETS{Foobar}{STUFF} | |
%% | |
%% \MAP{Foobar}{OPTION} | |
%% \SURFACEMAP{Foobar}{OPTION} | |
%% \REMAP{Foobar} | |
%% \RESURFACEMAP{Foobar} | |
%% \CHANGEMAP{Foobar}{OPTION} | |
%% \CHANGESURFACEMAP{Foobar}{OPTION} | |
%% | |
%% \NEW{Foobar}{\command}{STUFF} | |
%% \INSTANCE{Foobar}{STUFF}{OPTION} | |
%% \EVERY{Foobar}{OPTION} | |
%% | |
%% Available in "STUFF" and "OPTION": | |
%% | |
%% \MYTYPESTRING | |
%% \MYPARENTSTRING | |
%% \MYCOMMAND | |
%% \ME | |
%% \MYMAP | |
%% \PARENT | |
%% \PARENTMAP | |
%% \PARENTSURFACEMAP | |
%% | |
%% \F \field | |
%% \FD \field {defaultval} | |
%% \FS \field {val} | |
%% \s \field {val} | |
%% \rs \field {val} | |
%% \sd \field {defaultval} | |
%% \append \field {val} | |
%% \prepend \field {val} | |
%% | |
%% \ADD{STUFF} | |
%% \RESET | |
%% | |
%% \toss[num]{stuff} | |
%% | |
%% \SORT{Foobar}{sort}{field}{Datatype macros}{OPTION} | |
%% \MAKESORT{Foobar}{sort}{field}{Datatype macros}{NAME} | |
%% \USESORT{Foobar}{NAME}{OPTION} | |
%% | |
%% options for "sort": | |
%% num< | |
%% num> | |
%% alpha< | |
%% alpha> | |
%% ascii< | |
%% ascii> | |
%% | |
%% \SIFT{Foobar}{sift}{field}{value}{Datatype macros}{OPTION} | |
%% \MAKESIFT{Foobar}{sift}{field}{value}{Datatype macros}{NAME} | |
%% \USESIFT{Foobar}{NAME}{OPTION} | |
%% | |
%% options for "sift": | |
%% = | |
%% != | |
%% num< | |
%% num> | |
%% num<= | |
%% num>= | |
%% alpha< | |
%% alpha> | |
%% alpha<= | |
%% alpha>= | |
%% ascii< | |
%% ascii> | |
%% ascii<= | |
%% ascii>= | |
%% | |
%%%%% | |
%%%%% | |
%% allowing ":" to be used in TeX command names, for this file, so | |
%% "internal" commands that use it can't be used directly/messed | |
%% with outside of this file. | |
\catcode`\:11\relax | |
%%%%% | |
%% Datatypes act like "Classes" in object oriented programming. They | |
%% can "inherit" parent types ("superclasses"). Datatype macros are | |
%% effectively "objects" or "instances" of the given Datatype. | |
%%%%% | |
%%%%% | |
%% \:rf return first | |
%% \:df delete first | |
%% lots of things use this | |
\def\:singletst{\futurelet\:tmp\::singletst} | |
\long\def\::singletst#1#2\:delim{\def\:tmp{#2}} | |
%% defines \:tmp to be the first value | |
\def\:rf#1{% | |
\long\def\:tmp\:i##1##2\:delim{\def\:tmp{##1}}% | |
\ifx#1\empty\else\expandafter\:tmp#1\:delim\fi} | |
%% removes first value | |
\def\:df#1{% | |
\long\def\:tmp\:i##1##2\:delim{\def#1{##2}}% | |
\ifx#1\empty\else\expandafter\:tmp#1\:delim\fi% | |
\def\:tmp{}} | |
%% generic append | |
\long\def\:ap#1#2{% | |
\expandafter\edef\expandafter#1\expandafter{% | |
\expandafter\unexpanded\expandafter{#1#2}}} | |
%% appends #2 to #1 using command #3 (\global or \relax) | |
\long\def\::al#1#2#3{% | |
\def\:tmp{#2}% | |
\unless\ifx\:tmp\empty | |
\:singletst#2\:delim% | |
\else% | |
\let\:tmp\relax% | |
\fi% | |
\ifx\:tmp\empty% | |
#3\:ap#1{\:i#2}% | |
\else% | |
#3\:ap#1{\:i{#2}}% | |
\fi} | |
%%%%% | |
%% \:typetest{string}{definition} | |
\long\def\:typetest#1#2{% | |
\edef\:tmp{#1}\ifx\:tmp\empty\er:tblank\let\:tmp\empty\else% | |
\ifcsname#1:t\endcsname% | |
\edef\:ti{\csname#1:t\endcsname}% | |
\edef\:pi{\csname\csname\:ti :p\endcsname :t\endcsname}% | |
#2% | |
\else% | |
\er:tunknown{#1}\let\:tmp\empty% | |
\fi\fi} | |
%%%%% | |
%% \toss[num]{stuff} | |
%% \stoss{stuff} | |
%% \etoss{stuff} | |
%% \preprocess{stuff} | |
\def\toss{\futurelet\:nxt\:toss} | |
\def\:toss{% | |
\ifx\:nxt[\def\:nxt{\::toss}% | |
\else\def\:nxt{\::toss[1]}% | |
\fi\:nxt} | |
\long\def\::toss[#1]#2{% | |
\ifnum#1<0\relax% | |
\else\ifnum#1=0\relax% | |
\def\:nxt{#2}% | |
\else\ifnum#1=1\relax% | |
\def\:nxt{\etoss{#2}}% | |
\else% | |
\:count=#1\advance\:count by-1\relax% | |
\expandafter\def\expandafter\:nxt\expandafter{\expandafter\stoss% | |
\expandafter{\expandafter\::toss\expandafter[\the\:count]{#2}}}% | |
\fi\fi\fi\:nxt} | |
\def\stoss{\:eqaf} | |
\long\def\etoss#1{\:toks{}\start:aft#1\end:aft} | |
\long\def\preprocess#1{\begingroup\etoss{#1}\endgroup} | |
\def\?{\let\:sptoken= }\? | |
\def\:ob{{\iffalse}\fi} | |
\def\:cb{\iffalse{\fi}} | |
\def\:clear{% | |
\ifx\:nxt?\let\:nxt\:suckQ\else\let\:nxt\relax\fi\:nxt} | |
\def\:suckQ#1{\?} | |
\def\:norm:aft{% | |
\def\?{\futurelet\:nxt\:clear}% | |
\long\def\X##1{##1}% | |
} | |
\def\:in:aft{% | |
\def\?{\?}% | |
\long\def\X{\X}% | |
} | |
\:norm:aft | |
\def\end:aft{\end:aft}% | |
\def\end::aft{\end::aft}% | |
\def\start:aft{\:in:aft\futurelet\:nxt\:aft} | |
\def\:aft{\let\:tmp\relax% | |
\ifx\:nxt\:sptoken\def\:tmp{\:dospace}% | |
\else\ifx\:nxt\bgroup\def\:tmp{\:dogrp}% | |
\else\ifx\:nxt\end:aft\def\:tmp{\:doend}% | |
\else\ifx\:nxt\end::aft\def\:tmp{\::doend}% | |
\else\ifx\:nxt\?\def\:tmp{\:doquery}% | |
\else\ifx\:nxt\X\def\:tmp{\:doprotect}% | |
\else\def\:tmp{\:dotok}% | |
\fi\fi\fi\fi\fi\fi\:tmp} | |
\def\:dospace#1 {\:toks\expandafter{\the\:toks\space}\start:aft} | |
\long\def\:dogrp#1{% | |
\def\:tmp{#1}% | |
\ifx\:tmp\empty\else\:singletst#1\:delim\fi% | |
\ifx\:tmp\empty\let\:tmp#1\else\let\:tmp\empty\fi% | |
\ifx\:tmp\bgroup\:toks\expandafter{\the\:toks\noexpand#1}% | |
\else% | |
\:toks\expandafter{\the\:toks\:ob}% | |
\start:aft#1\end::aft% | |
\:toks\expandafter{\the\:toks\:cb}% | |
\fi\let\:tmp\relax\start:aft} | |
\def\:doend#1{% | |
\edef\:tmp{\the\:toks}% | |
\:toks{}% | |
\edef\:tmp{\:tmp}% | |
\expandafter\:eqaf\expandafter{\:tmp}% | |
\def\:tmp{}} | |
\def\::doend#1{\:norm:aft}% | |
\def\:doquery#1{\futurelet\:nxt\::doquery} | |
\def\::doquery{% | |
\ifx\:nxt?\def\:nxt##1{\:toks\expandafter{\the\:toks\noexpand\?}\start:aft}% | |
\else\long\def\:nxt##1{% | |
\expandafter\start:aft\expandafter\X\expandafter{##1}}% | |
\fi\:nxt} | |
\long\def\:doprotect#1#2{% | |
\:toks\expandafter{\the\:toks\unexpanded{#2}}\start:aft} | |
\long\def\:dotok#1{\:toks\expandafter{\the\:toks\noexpand#1}\start:aft} | |
%% \:eqaf | |
%% \:dqaf | |
\gdef\:qnum{-1} | |
\gdef\:qlst{} | |
\long\def\:eqaf#1{% | |
\ifx\:qlst\empty% | |
\:count=\:qnum\relax% | |
\advance\:count1% | |
\xdef\:qnum{\the\:count}% | |
\expandafter\def\expandafter\:nxt\expandafter{\csname :q\:qnum\endcsname}% | |
\else% | |
\:rf\:qlst% | |
\let\:nxt\:tmp% | |
\:df\:qlst% | |
\global\let\:qlst\:qlst% | |
\fi% | |
\expandafter\xdef\:nxt{\unexpanded{#1}}% | |
\aftergroup\:dqaf\expandafter\aftergroup\:nxt} | |
\def\:dqaf#1{% | |
\def\:tmp##1{% | |
\expandafter\def\expandafter\:tmp\string\:q####1\:delim{\def\:tmp{####1}}% | |
\expandafter\:tmp\string##1\:delim}% | |
\:tmp#1% | |
\:count=\:qnum% | |
\advance\:count-1% | |
\ifnum\:count=\:tmp% | |
\xdef\:qnum{\the\:count}% | |
\else% | |
\::al\:qlst#1\global% | |
\fi% | |
\let\:tmp#1% | |
\global\let#1\relax% | |
\:tmp} | |
%%%%% | |
%% \DECLARETYPE{Foo} | |
%% Creates a new Datatype, with "Foo" as the typestring. | |
\def\DECLARETYPE#1{\DECLARESUBTYPE{#1}{}} | |
%%%%% | |
%% \DECLARESUBTYPE{Foobar}{Foo} | |
%% Creates a new Datatype "Foobar" with Datatype "Foo" as the "parent" | |
%% Datatype. "Inherits" the contents of Foo's \PRESETS and \POSTSETS, | |
%% is included in the list of \EVERY{Foo}, and by default, Foobar macros | |
%% are initially mapped to an equivalent of \PARENTMAP. | |
\def\:tc{0} | |
\def\DECLARESUBTYPE#1#2{% | |
\def\:tmp{#1}\ifx\:tmp\empty\er:tblank% | |
\else\ifcsname#2:t\endcsname% | |
\ifcsname#1:t\endcsname% | |
\:typgenwarn{Datatype "#1" is already defined.\:mb Command ignored.}% | |
\else% | |
\expandafter\edef\csname#1:t\endcsname{\:tc}% | |
\:count\:tc% | |
\advance\:count1% | |
\edef\:tc{\the\:count}% | |
\expandafter\let\expandafter\:ti\csname#1:t\endcsname% | |
\expandafter\let\expandafter\:pi\csname#2:t\endcsname% | |
\expandafter\ifx\csname\:ti :p\endcsname\empty% | |
\expandafter\def\csname\:ti :pdo\endcsname{% | |
\:typwarn{\string\PARENT: "#1" has no parent.}}% | |
\expandafter\def\csname\:ti :phd\endcsname{% | |
\:typwarn{\string\PARENTMAP: "#1" has no parent.}}% | |
\expandafter\def\csname\:ti :phh\endcsname{% | |
\:typwarn{\string\PARENTSURFACEMAP: "#1" has no parent.}}% | |
\else% | |
\csname\:pi :ca\endcsname{#1}% | |
\begingroup% | |
\aftergroup\def\expandafter\aftergroup\csname\:ti :pdo\endcsname% | |
\aftergroup{\aftergroup\let\aftergroup\PARENT% | |
\expandafter\aftergroup\csname\:pi :pdo\endcsname% | |
\expandafter\aftergroup\csname\:pi :rdo\endcsname% | |
\aftergroup\let\aftergroup\PARENT% | |
\expandafter\aftergroup\csname\:ti :pdo\endcsname\aftergroup}% | |
\endgroup% | |
\begingroup% | |
\aftergroup\def\expandafter\aftergroup\csname\:ti :phd\endcsname% | |
\aftergroup{\aftergroup\let\aftergroup\PARENTMAP% | |
\expandafter\aftergroup\csname\:pi :phd\endcsname% | |
\expandafter\aftergroup\csname\:pi :rhd\endcsname% | |
\aftergroup\let\aftergroup\PARENTMAP% | |
\expandafter\aftergroup\csname\:ti :phd\endcsname\aftergroup}% | |
\endgroup% | |
\begingroup% | |
\aftergroup\def\expandafter\aftergroup\csname\:ti :phh\endcsname% | |
\aftergroup{\aftergroup\let\aftergroup\PARENTSURFACEMAP% | |
\expandafter\aftergroup\csname\:pi :phh\endcsname% | |
\expandafter\aftergroup\csname\:pi :rhh\endcsname% | |
\aftergroup\let\aftergroup\PARENTSURFACEMAP% | |
\expandafter\aftergroup\csname\:ti :phh\endcsname\aftergroup}% | |
\endgroup% | |
\fi% | |
\expandafter\def\csname\:ti :p\endcsname{#2}% | |
\expandafter\def\csname\:ti :ct\endcsname{}% | |
\begingroup% | |
\aftergroup\def\expandafter\aftergroup\csname\:ti :ca\endcsname% | |
\aftergroup##\aftergroup1\aftergroup{% | |
\aftergroup\::al\expandafter\aftergroup\csname\:ti :ct\endcsname% | |
\aftergroup{\aftergroup##\aftergroup1\aftergroup}\aftergroup\relax% | |
\expandafter\aftergroup\csname\:pi :ca\endcsname% | |
\aftergroup{\aftergroup##\aftergroup1\aftergroup}\aftergroup}% | |
\endgroup% | |
\expandafter\def\csname\:ti :lst\endcsname{}% | |
\begingroup% | |
\aftergroup\def\expandafter\aftergroup\csname\:ti :add\endcsname% | |
\aftergroup##\aftergroup1\aftergroup{% | |
\aftergroup\::al\expandafter\aftergroup\csname\:ti :lst\endcsname% | |
\aftergroup##\aftergroup1\aftergroup\relax% | |
\expandafter\aftergroup\csname\:pi :add\endcsname% | |
\aftergroup##\aftergroup1\aftergroup}% | |
\endgroup% | |
\expandafter\expandafter\expandafter\def\expandafter\expandafter% | |
\csname\:ti :pre\endcsname\expandafter{\csname\:pi :pre\endcsname}% | |
\expandafter\expandafter\expandafter\def\expandafter\expandafter% | |
\csname\:ti :pst\endcsname\expandafter{\csname\:pi :pst\endcsname}% | |
\MAP{#1}{\PARENT}% | |
\fi% | |
\else% | |
\:typgenerr{Datatype "#2" isn't defined; hence it can't\:mb be | |
used as a parent for datatype "#1."}{Declare "#2" before | |
declaring any children.}% | |
\fi\fi} | |
%%%%% | |
%% Datatype macros each contain a set of "STUFF" that effectively gets | |
%% executed inside the macro (inside a TeX group) every time it is used. | |
%% | |
%% The "STUFF" comes in the following order: | |
%% | |
%% (parenttype's \PRESETS "STUFF") | |
%% \PRESETS "STUFF" | |
%% "STUFF" from \NEW | |
%% (parenttype's \POSTSETS "STUFF") | |
%% \POSTSETS "STUFF" | |
%% | |
%% The "STUFF" from \NEW is unique to each macro, unlike the rest, | |
%% which is the same for all macros of a given Datatype. | |
%% | |
%% For all "STUFF" unnested (La)TeX command definitions need not use | |
%% doubled #'s for args (i.e. don't treat commands with "STUFF" as | |
%% TeX macro definitions). | |
%%%%% | |
%%%%% | |
%% \PRESETS{Foobar}{STUFF} | |
%% Sets \PRESETS "STUFF" for Datatype Foobar | |
\long\def\PRESETS#1#2{\:typetest{#1}{% | |
\expandafter\:ap\csname\:ti :pre\endcsname{#2}% | |
}} | |
%%%%% | |
%% \POSTSETS{Foobar}{STUFF} | |
%% Sets \POSTSETS "STUFF" for Datatype Foobar | |
\long\def\POSTSETS#1#2{\:typetest{#1}{% | |
\expandafter\:ap\csname\:ti :pst\endcsname{#2}% | |
}} | |
%%%%% | |
%% Datatype macros take a single argument (_required_braces_), that | |
%% is "OPTION." "OPTION" is expanded inside the macro, effectively | |
%% right after all of "STUFF." So, any defined macros in "STUFF" are | |
%% available (i.e. a field declared in \PRESETS with "\F \somefield" | |
%% and set in \NEW with "\s \somefield {somevalue}" can be used in | |
%% "OPTION"). | |
%% | |
%% If "OPTION" is blank (i.e. "\sometypemacro{}" not "\sometypemacro{ }") | |
%% the current "mapping" of the Datatype is expanded instead (changed by | |
%% \MAP, etc). | |
%% | |
%% In "OPTION," the same (non-)rules apply to nesting macro definitions | |
%% as for "STUFF." | |
%% | |
%% In both "STUFF" and "OPTION" the following commands are available: | |
%% | |
%% \MYTYPESTRING -> i.e. "Foobar" for Foobar Datatypes | |
%% \MYPARENTSTRING -> i.e. "Foo" for Foobar Datatypes | |
%% \MYCOMMAND -> the command name for the Datatype Macro | |
%% \ME -> same as \MYCOMMAND for normal macros, see "\INSTANCE" below | |
%% \MYMAP -> the mapping of the Datatype (only available in "OPTION") | |
%% \PARENT -> parenttype's mapping | |
%%%%% | |
\long\def\CHILDTYPES#1#2{% | |
\edef\:dotypes{\unexpanded{#2}}% | |
\def\:i##1{\def\TYPE{##1}\:dotypes}% | |
\expandafter\csname\csname#1:t\endcsname :ct\endcsname% | |
\let\TYPE\empty} | |
\long\def\BRANCHTYPES#1#2{% | |
\edef\:dotypes{\unexpanded{#2}}% | |
\def\:i##1{\def\TYPE{##1}\:dotypes}% | |
\:i{#1}% | |
\expandafter\csname\csname#1:t\endcsname :ct\endcsname% | |
\let\TYPE\empty} | |
%%%%% | |
%% \MAP{Foobar}{OPTION} | |
%% Sets the mapping of Foobar Datatypes to "OPTION" | |
\long\def\MAP#1#2{\:typetest{#1}{% | |
\CHANGEMAP{#1}{#2}% | |
\CHANGESURFACEMAP{#1}{#2}% | |
\REMAP{#1}% | |
}} | |
%%%%% | |
%% \SURFACEMAP{Foobar}{OPTION} | |
%% "Temporarily" sets the mapping of Foobar Datatypes. | |
%% The current mapping is stored, and the mapping is reset to it for | |
%% Foobar macros _nested inside_ Foobar macros, i.e. \SURFACEMAP's | |
%% mapping only applies to the "first level" (present level) of Foobar | |
%% macro nesting. | |
%% | |
%% Repeated uses of \SURFACEMAP only change the \FISTMAP mapping, not the | |
%% stored mapping. \MAP changes both the current mapping and the stored | |
%% mapping and "turns off" the effects of \SURFACEMAP. | |
\long\def\SURFACEMAP#1#2{\:typetest{#1}{% | |
\CHANGESURFACEMAP{#1}{#2}% | |
\RESURFACEMAP{#1}% | |
}} | |
%%%%% | |
%% \REMAP{Foobar} | |
%% "Turns off" the effect of \SURFACEMAP, i.e. restores the current | |
%% mapping to the stored mapping. | |
\long\def\REMAP#1{\:typetest{#1}{% | |
\expandafter\expandafter\expandafter\def\expandafter\expandafter% | |
\csname\:ti :do\endcsname\expandafter{\csname\:ti :rhd\endcsname}% | |
}} | |
%%%%% | |
%% \RESURFACEMAP{Foobar} | |
\long\def\RESURFACEMAP#1{\:typetest{#1}{% | |
\expandafter\expandafter\expandafter\def\expandafter\expandafter% | |
\csname\:ti :do\endcsname\expandafter{\csname\:ti :rhh\endcsname}% | |
}} | |
%%%%% | |
%% \CHANGEMAP{Foobar} | |
\long\def\CHANGEMAP#1#2{\:typetest{#1}{% | |
\expandafter\edef\csname\:ti :hd\endcsname{\unexpanded{#2}}% | |
}} | |
%%%%% | |
%% \CHANGESURFACEMAP{Foobar} | |
\long\def\CHANGESURFACEMAP#1#2{\:typetest{#1}{% | |
\expandafter\edef\csname\:ti :hh\endcsname{\unexpanded{#2}}% | |
}} | |
%%%%% | |
%% Datatype macros, which act like "objects" or "instances" of | |
%% Datatypes, can be created by \NEW. \NEW can include "STUFF" which | |
%% is unique to the given macro. | |
%% | |
%% When a Datatype macro is created as such, it is added to a list of | |
%% macros for the given Datatype, which is accessible via \EVERY. If | |
%% a Datatype has a parenttype, macros of that Datatype are also added | |
%% to the \EVERY list of the parent Datatype. | |
%% | |
%% With \INSTANCE, it is possible to create and use, at the same time, a | |
%% single, unique, and temporary "instance" of a Datatype. "STUFF" for | |
%% \INSTANCE would substitute for "STUFF" in an equivalent use of \NEW, | |
%% while "OPTION" would work the same as for a macro created by \NEW, | |
%% so \INSTANCE{Foobar}{STUFF}{OPTION} would have similar effect to | |
%% \NEW{Foobar}{\temp}{STUFF} \temp{OPTION}. However, it does not | |
%% create an actual macro. This "temporary instance" is not added to | |
%% any \EVERY list. | |
%%%%% | |
%%%%% | |
%% \NEW{Foobar}{\command}{STUFF} | |
%% Creates \command{} as a Foobar macro, with "STUFF" | |
\def\:ic{0} | |
\long\def\NEW#1#2#3{\:typetest{#1}{% | |
\:gotrue% | |
\ifdefined#2% | |
\ifx#2\relax\else% | |
\:typgenerr{"\string#2" is already defined.}{"\string#2" is | |
already defined (by \string\def, \string\newcommand, etc).\:mb | |
Or it may be a default (La)TeX macro. Or it may already be a | |
\:mb datatype macro. A datatype macroname must be unique.}% | |
\:gofalse% | |
\fi% | |
\fi% | |
\if:go% | |
\csname\:ti :add\endcsname#2% | |
\expandafter\let\csname\string#2:ic\endcsname\:ic% | |
\expandafter\def\csname\:ic :l\endcsname{}% | |
\expandafter\edef\csname\:ic :pr\endcsname{% | |
\noexpand\:prep{\:ic}{#1}\noexpand#2{\unexpanded{#3}}}% | |
\expandafter\edef\csname\:ic :o\endcsname{% | |
\unexpanded{\let\:opt\empty}\csname\:ic :op\endcsname}% | |
\expandafter\edef\csname\:ic :op\endcsname{% | |
\noexpand\futurelet\noexpand\:nxt\csname\:ic :opt\endcsname}% | |
\expandafter\edef\csname\:ic :opt\endcsname{% | |
\unexpanded{\ifx\:nxt[\let#2}\csname\:ic :opti\endcsname% | |
\unexpanded{\else\let#2}\csname\:ic :optio\endcsname% | |
\noexpand\fi\noexpand#2}% | |
\expandafter\edef\csname\:ic :opti\endcsname[##1]{% | |
\noexpand\def\noexpand#2{\expandafter\noexpand\csname\:ic :o\endcsname}% | |
\noexpand\:ap\noexpand\:opt{##1}% | |
\expandafter\noexpand\csname\:ic :op\endcsname}% | |
\expandafter\edef\csname\:ic :optio\endcsname##{% | |
\noexpand\def\noexpand#2{\expandafter\noexpand\csname\:ic :o\endcsname}% | |
\csname\:ic :c\endcsname}% | |
\edef#2{\expandafter\noexpand\csname\:ic :o\endcsname}% | |
\long\expandafter\edef\csname\:ic :c\endcsname##1{% | |
\noexpand\begingroup\expandafter\noexpand\csname\:ic :pr\endcsname% | |
\noexpand\:doarg{##1}\noexpand\endgroup}% | |
\:count\:ic% | |
\advance\:count1% | |
\edef\:ic{\the\:count}% | |
\fi% | |
}} | |
\long\def\:EXTEND#1{\:typgenerr{Don't use \string\EXTEND on non Datatypes.}% | |
{It has no context.}} | |
\long\def\::EXTEND#1{% | |
\endgroup\expandafter\:ap\csname\:id :l\endcsname{#1}% | |
\begingroup} | |
\long\def\EXTEND#1#2{#1{\:EXTEND{#2}}} | |
\long\def\:prep#1#2#3#4{% | |
\:count\:lvl% | |
\advance\:count1% | |
\edef\:lvl{\the\:count}% | |
\edef\:ti{\csname#2:t\endcsname}% | |
\:remap{#2}% | |
\edef\:ti{\csname#2:t\endcsname}% | |
\expandafter\let\expandafter\:mydo\csname\:ti :rdo\endcsname% | |
\long\def\OPTIONHOOK##1{##1}% | |
\def\MYTYPESTRING{#2}% | |
\edef\MYPARENTSTRING{\csname\:ti :p\endcsname}% | |
\def\MYCOMMAND{#3}% | |
\let\::opt\:opt% | |
\ifx\::opt\empty% | |
\let\ME\MYCOMMAND% | |
\else% | |
\expandafter\def\expandafter\ME\expandafter{% | |
\expandafter#3\expandafter[\::opt]}% | |
\fi% | |
\let\MYMAP\:mydo% | |
\stoss{\def\:id{#1}}% | |
\expandafter\let\expandafter\PARENT\csname\:ti :pdo\endcsname% | |
\expandafter\let\expandafter\PARENTMAP\csname\:ti :phd\endcsname% | |
\expandafter\let\expandafter\PARENTSURFACEMAP\csname\:ti :phh\endcsname% | |
\let\F\:F% | |
\let\FD\:FD% | |
\let\FS\:FS% | |
\let\s\:s% | |
\let\rs\:rs% | |
\let\sd\:sd% | |
\let\append\:append% | |
\let\prepend\:prepend% | |
\edef\:presentfont{\the\font}\nullfont% | |
\let\::par\par\let\par\relax% | |
\csname\:ti :pre\endcsname% | |
#4% | |
\csname#1:l\endcsname% | |
\::opt% | |
\csname\:ti :pst\endcsname% | |
\let\par\::par\:presentfont% | |
\let\ADD\:ADD% | |
%\def\RESET{\:reset}% | |
\let\:EXTEND\::EXTEND% | |
} | |
\long\def\:remap#1{% | |
\expandafter\expandafter\expandafter\let\expandafter\expandafter% | |
\csname\:ti :rdo\endcsname\csname\:ti :do\endcsname% | |
\expandafter\expandafter\expandafter\let\expandafter\expandafter% | |
\csname\:ti :rhd\endcsname\csname\:ti :hd\endcsname% | |
\expandafter\expandafter\expandafter\let\expandafter\expandafter% | |
\csname\:ti :rhh\endcsname\csname\:ti :hh\endcsname% | |
\expandafter\ifx\csname\:ti :p\endcsname\empty% | |
\BRANCHTYPES{#1}{\REMAP{\TYPE}}% | |
\else% | |
\edef\:tmp{\csname\:ti :p\endcsname}% | |
\edef\:ti{\csname\:tmp :t\endcsname}% | |
\expandafter\:remap\expandafter{\:tmp}% | |
\fi} | |
\long\def\:doarg#1{% | |
\edef\:tmp{\unexpanded{#1}}% | |
\ifx\:tmp\empty\let\:tmp\:mydo% | |
\fi\expandafter\OPTIONHOOK\expandafter{\:tmp}} | |
%%%%% | |
%% \INSTANCE{Foobar}{STUFF}{OPTION} | |
%% Creates a temporary instance of Foobar, with "STUFF" and "OPTION." | |
%% For a use of \INSTANCE{Foobar}, \MYCOMMAND only corresponds to | |
%% "\INSTANCE{Foobar}," i.e. not enough information to recreate the | |
%% complete uniqueness of the use of \INSTANCE. \ME, however, is defined | |
%% as "\INSTANCE{Foobar}{STUFF}." | |
%% | |
%% Like macros created by \NEW, braces for "OPTION" are required, even | |
%% if "OPTION" is blank (TeX gives an error if there are no braces). | |
\long\def\INSTANCE#1#2{\:typetest{#1}{% | |
\expandafter\def\csname :\:lvl\endcsname##1{% | |
\:count\:lvl% | |
\advance\:count1% | |
\edef\:lvl{\the\:count}% | |
\edef\:ti{\csname#1:t\endcsname}% | |
\:remap{#1}% | |
\edef\:ti{\csname#1:t\endcsname}% | |
\expandafter\let\expandafter\:mydo\csname\:ti :rdo\endcsname% | |
\long\def\OPTIONHOOK####1{####1}% | |
\def\MYTYPESTRING{#1}% | |
\edef\MYPARENTSTRING{\csname\:ti :p\endcsname}% | |
\def\MYCOMMAND{\INSTANCE{#1}}% | |
\let\::opt\:opt | |
\ifx\::opt\empty% | |
\def\ME{\INSTANCE{#1}{#2}}% | |
\else% | |
\edef\ME{\unexpanded{\INSTANCE{#1}{#2}}% | |
[\expandafter\unexpanded\expandafter{\::opt}]}% | |
\fi% | |
\let\MYMAP\:mydo% | |
\expandafter\let\expandafter\PARENT\csname\:ti :pdo\endcsname% | |
\expandafter\let\expandafter\PARENTMAP\csname\:ti :phd\endcsname% | |
\expandafter\let\expandafter\PARENTSURFACEMAP\csname\:ti :phh\endcsname% | |
\let\F\:F% | |
\let\FD\:FD% | |
\let\FS\:FS% | |
\let\s\:s% | |
\let\rs\:rs% | |
\let\sd\:sd% | |
\let\append\:append% | |
\let\prepend\:prepend% | |
\edef\:presentfont{\the\font}\nullfont% | |
\let\:par\par\let\par\relax% | |
\csname\:ti :pre\endcsname% | |
#2##1\::opt% | |
\csname\:ti :pst\endcsname% | |
\let\par\:par\:presentfont% | |
%\long\def\ADD####1{\endgroup\begingroup\csname :\:lvl\endcsname{####1}}% | |
\long\def\ADD####1{####1}% | |
%\def\RESET{\endgroup\begingroup\csname :\:lvl\endcsname{}}% | |
\long\def\:EXTEND####1{}% | |
}% | |
\long\def\:tmp##1{% | |
\begingroup\csname :\:lvl\endcsname{}\:doarg{##1}\endgroup}% | |
}\INSTANCE:} | |
\def\INSTANCE:{\let\:opt\empty\INSTANCE::}% | |
\def\INSTANCE::{\futurelet\:nxt\INSTANCE:::} | |
\def\INSTANCE:::{\ifx\:nxt[\def\:nxt{\INSTANCE::::}% | |
\else\def\:nxt{\INSTANCE:::::}\fi\:nxt}% | |
\def\INSTANCE::::[#1]{\:ap\:opt{#1}\INSTANCE::}% | |
\def\INSTANCE:::::#{\:tmp}% | |
%%%%% | |
%% \EVERY{Foobar}{OPTION} | |
%% Effectively executes the entirety of the the "\EVERY list" for the | |
%% given Datatype, all with "OPTION" i.e. has same result as using all | |
%% the macros, in order created, with "OPTION" explicitly (not faked | |
%% with mappings). | |
\def\EVERY#1{\def\:tmp{#1}\EVERY:} | |
\def\EVERY:{\futurelet\:nxt\EVERY::} | |
\def\EVERY::{\ifx\:nxt[\def\:nxt{\EVERY:::} | |
\else\def\:nxt{\EVERY:::[]}\fi\:nxt} | |
\def\EVERY:::[#1]{\def\:opt{#1}\EVERY::::} | |
\def\EVERY::::#{\expandafter\EVERY:::::\expandafter{\:opt}} | |
\def\EVERY:::::#1#2{\:typetest{\:tmp}{% | |
\ifx\:opt\empty% | |
\def\:i##1{\:ap\:tmp{##1{#2}}}% | |
\else% | |
\def\:i##1{\:ap\:tmp{##1[#1]{#2}}}% | |
\fi% | |
\def\:tmp{\let\:tmp\empty}\csname\:ti :lst\endcsname}\:tmp} | |
%%%%% | |
%% Within Datatypes, the following commands are used to declare, set, | |
%% and manipulate "fields." Fields are essentially basic TeX macros, | |
%% i.e. are expanded the same. These commands are a good way to set up | |
%% error checking to catch typos, declare fields that must be explicitly | |
%% set before being used, set things with defaults, catch accidentally | |
%% setting them twice, etc. | |
%% | |
%% \F, \FD, and \FS are for declaring a field. They set internal flags | |
%% (separate from the actual field) that say the field exists as a field | |
%% and label which nesting level the field belongs to (so Datatype macros | |
%% nested inside one another, with matching field names, can avoid | |
%% conflicts). These flags are also used to determine whether or not | |
%% a field is declared with a "default" or has been explicitly set. | |
%% | |
%% \F declares a field, with no value. | |
%% | |
%% \FD declares a fields with a "default." | |
%% | |
%% \FS declares a field and explicitly sets it. | |
%% | |
%% \s, \rs, \append, and \prepend are for setting and manipulating | |
%% a field; they use the internal flags. | |
%% | |
%% \s explicitly sets the field to a value, and gives a warning (not an | |
%% error; it does the set anyway) if the field was already set (if it | |
%% was only a "default" no warning is given, if it was already explicitly | |
%% set, even to exactly the same as the default, it does warn). | |
%% | |
%% \rs explicitly sets the field, and doesn't give a warning if it has | |
%% already been explicitly set (i.e. a "reset"). | |
%% | |
%% \append and \prepend explicitly set the field by "adding" the given | |
%% value to the end or beginning of the field, respectively. If the | |
%% field has _not_ been explicitly set (default or no), they just | |
%% explicitly set the field to the value, i.e. they ignore and replace | |
%% a "default" value. | |
%% | |
%% In general, errors happen when a field is declared with a command | |
%% name that is already a field or (La)TeX macro, if a field is used | |
%% before having any value, or if \s, \rs, etc. are used on something | |
%% that has not been declared a field. | |
%%%%% | |
%% | |
\def\:deferr#1#2{ | |
\:typerr% | |
{\string#1 in "\string#2\string#1" in | |
\expandafter\noexpand\MYCOMMAND\:mb is already defined.}% | |
{\string#1 is already defined (by \string\def, \string\newcommand, | |
etc). \:mb Or it may be a default (La)TeX macro. Fields must be | |
unique.}}% | |
\def\:fielderr#1#2{ | |
\:typerr% | |
{\string#1 in "\string#2\string#1" in | |
\expandafter\noexpand\MYCOMMAND is already a field.}% | |
{\string#1 is already declared by \string\F, \string\FD, or | |
\string\FS. Fields must be unique.}}% | |
\def\:noferr#1#2{ | |
\:typerr% | |
{\string#1 in \expandafter\noexpand\MYCOMMAND has not been | |
declared a field.}% | |
{"\string#2\string#1" may just be a typo in | |
\expandafter\string\MYCOMMAND. Otherwise, \string#1 needs to be | |
\:mb declared by "\string\F\string#1" (or \string\FD, or | |
\string\FS).}}% | |
%%%%% | |
%% \F \field | |
%% Declares a field "\field" with no default (must be set before it is | |
%% expanded or it gives an error). Effectively defines the macro as the | |
%% error. | |
\def\:F#1{% | |
\:gofalse% | |
\ifdefined#1% | |
\ifcsname\string#1.w\endcsname% | |
\ifcsname\:ti\string#1\:lvl\endcsname% | |
\:fielderr{#1}{\F}% | |
\else% | |
\:gotrue% | |
\fi% | |
\else% | |
\:deferr{#1}{\F}% | |
\fi% | |
\else% | |
\:gotrue% | |
\fi% | |
\if:go% | |
\expandafter\def\csname\:ti\string#1\:lvl\endcsname{y}% | |
\expandafter\let\csname\string#1.w\endcsname\empty% | |
\def#1{\er:funset{#1}}% | |
\fi} | |
%%%%% | |
%% \FD \field {defaultval} | |
%% Declares \field with "defaultval" as the value, and sets the internal | |
%% flag to say the value is a "default." | |
\long\def\:FD#1#2{% | |
\:gofalse% | |
\ifdefined#1% | |
\ifcsname\string#1.w\endcsname% | |
\ifcsname\:ti\string#1\:lvl\endcsname% | |
\:fielderr{#1}{\FD}% | |
\else% | |
\:gotrue% | |
\fi% | |
\else% | |
\:deferr{#1}{\FD}% | |
\fi% | |
\else% | |
\:gotrue% | |
\fi% | |
\if:go% | |
\expandafter\def\csname\:ti\string#1\:lvl\endcsname{y}% | |
\expandafter\let\csname\string#1.w\endcsname\empty% | |
\def#1{#2}% | |
\fi} | |
%%%%% | |
%% \FS \field {val} | |
%% Declares \field, and explicitly sets it to "val" | |
\long\def\:FS#1#2{% | |
\:gofalse% | |
\ifdefined#1% | |
\ifcsname\string#1.w\endcsname% | |
\ifcsname\:ti\string#1\:lvl\endcsname% | |
\:fielderr{#1}{\FS}% | |
\else% | |
\:gotrue% | |
\fi% | |
\else% | |
\:deferr{#1}{\FS}% | |
\fi% | |
\else% | |
\:gotrue% | |
\fi% | |
\if:go% | |
\expandafter\def\csname\:ti\string#1\:lvl\endcsname{y}% | |
\expandafter\def\csname\string#1.w\endcsname{y}% | |
\def#1{#2}% | |
\fi} | |
%%%%% | |
%% \s \field {val} | |
%% Explicitly sets \field to "val" and gives a warning (but does it | |
%% anyway) if the \field has already been explicitly set. | |
\long\def\:s#1#2{% | |
\ifdefined#1% | |
\ifcsname\:ti\string#1\:lvl\endcsname% | |
\expandafter\ifx\csname\string#1.w\endcsname\empty\else% | |
\:typwarn{\string#1 in \expandafter\noexpand\MYCOMMAND is | |
\noexpand\s more than once.}% | |
\fi% | |
\def#1{#2}% | |
\expandafter\def\csname\string#1.w\endcsname{y}% | |
\else% | |
\:deferr{#1}{\s}% | |
\fi% | |
\else% | |
\:noferr{#1}{\s}% | |
\fi} | |
%%%%% | |
%% \rs \field {val} | |
%% Explicitly sets \field to "val" | |
\long\def\:rs#1#2{% | |
\ifdefined#1% | |
\ifcsname\:ti\string#1\:lvl\endcsname% | |
\def#1{#2}% | |
\expandafter\def\csname\string#1.w\endcsname{y}% | |
\else% | |
\:deferr{#1}{\rs}% | |
\fi% | |
\else% | |
\:noferr{#1}{\rs}% | |
\fi} | |
%%%%% | |
%% \sd \field {default} | |
%% sets \field default to "default" | |
\long\def\:sd#1#2{% | |
\ifdefined#1% | |
\ifcsname\:ti\string#1\:lvl\endcsname% | |
\expandafter\ifx\csname\string#1.w\endcsname\empty% | |
\def#1{#2}% | |
\fi% | |
\else% | |
\:deferr{#1}{\sd}% | |
\fi% | |
\else% | |
\:noferr{#1}{\sd}% | |
\fi} | |
%%%%% | |
%% \append \field {val} | |
%% Explicitly sets \field by appending "val" to the end of the previous | |
%% expansion of \field. If field has not been explicitly set before, | |
%% it just explicitly sets it to "val" (possibly replacing a "default"). | |
\long\def\:append#1#2{% | |
\ifdefined#1% | |
\ifcsname\:ti\string#1\:lvl\endcsname% | |
\expandafter\ifx\csname\string#1.w\endcsname\empty% | |
\edef#1{\unexpanded{#2}}% | |
\else% | |
\:ap#1{#2}% | |
\fi% | |
\expandafter\def\csname\string#1.w\endcsname{y}% | |
\else% | |
\:deferr{#1}{\append}% | |
\fi% | |
\else% | |
\:noferr{#1}{\append}% | |
\fi} | |
%%%%% | |
%% \prepend \field {val} | |
%% Explicitly sets \field by prepending "val" to the beginning of | |
%% the previous expansion of \field. If field has not been explicitly set | |
%% before, it just explicitly sets it to "val" (possibly replacing a | |
%% "default"). | |
\long\def\:prepend#1#2{% | |
\ifdefined#1% | |
\ifcsname\:ti\string#1\:lvl\endcsname% | |
\expandafter\ifx\csname\string#1.w\endcsname\empty% | |
\def#1{#2}% | |
\else% | |
\edef\:tmp{\unexpanded{#2}}% | |
\expandafter\:ap\expandafter\:tmp\expandafter{#1}% | |
\let#1\:tmp% | |
\fi% | |
\expandafter\def\csname\string#1.w\endcsname{y}% | |
\else% | |
\:deferr{#1}{\prepend}% | |
\fi% | |
\else% | |
\:noferr{#1}{\prepend}% | |
\fi} | |
%%%%% | |
%% For a Datatype macro, commands etc. in "OPTION" are not "remembered" | |
%% from use to use of the macro, and the rest of "STUFF" in the macro | |
%% are either declared for all macros of that Datatype or declared once | |
%% for that macro by \NEW. Therefore, inside macros, the following | |
%% commands are available that allow commands etc. to be "remembered" | |
%% from use to use, effectively allowing "mutability." | |
%% | |
%% \ADD and \GLOBALADD add things to the stored "memory" of "STUFF" | |
%% for the Datatype macro they are used in. \RESET and \GLOBALRESET | |
%% cause that "remembered STUFF" to be blanked out. | |
%%%%% | |
%%%%% | |
%% \ADD{STUFF} | |
%% Adds "STUFF" to the "remember STUFF" for the macro it is used in. | |
%% "STUFF" also takes effect immediately. | |
\long\def\:ADD#1{% | |
#1\expandafter\EXTEND\expandafter{\ME}{#1}% | |
\expandafter\stoss\expandafter{\expandafter\EXTEND\expandafter{\ME}{#1}}} | |
% \endgroup% | |
% \expandafter\:ap\csname\:id :l\endcsname{#1}% | |
% \begingroup% | |
% \csname\:id :pr\endcsname} | |
%%%%% | |
%% \RESET | |
%% Blanks out the "remembered STUFF" for the macro it is used in. Also | |
%% "restarts" the use of the Datatype macro, such that things declared, | |
%% set, or defined etc. so far local to the macro no longer have effect | |
%% (i.e. "\FS \field {blah} \RESET \field" will give the error that | |
%% "\field" is an undefined control sequence). | |
%\def\:reset{% | |
% \endgroup% | |
% \expandafter\def\csname\:id :l\endcsname{}% | |
% \begingroup% | |
% \csname\:id :pr\endcsname} | |
%%%%% | |
%% \SORT{Foobar}{sort}{field}{Datatype macros}{OPTION} | |
%% | |
\long\def\SORT#1#2#3#4#5{\:typetest{#1}{% | |
\:checksortarg{#2}% | |
\let\:tmp\relax% | |
\if:go% | |
\:dosort{#1}{#2}{#3}{#4}% | |
\def\:i##1{\:ap\:tmp{##1{#5}}}% | |
\def\:tmp{\let\:tmp\empty}% | |
\:sorted% | |
\global\let\:sorted\relax% | |
\fi% | |
}\:tmp} | |
%%%%% | |
%% \MAKESORT{Foobar}{sort}{field}{Datatype macros}{NAME} | |
%% | |
\long\def\MAKESORT#1#2#3#4#5{\:typetest{#1}{% | |
\:checksortarg{#2}% | |
\if:go% | |
\ifcsname#5:SORT\endcsname% | |
\:typgenwarn{"#5" reused as sorted list name.}% | |
\fi% | |
\:dosort{#1}{#2}{#3}{#4}% | |
\expandafter\let\csname#5:SORT\endcsname\:sorted% | |
\global\let\:sorted\relax% | |
\fi% | |
}} | |
%%%%% | |
%% \USESORT{NAME}{OPTION} | |
%% | |
\long\def\USESORT#1#2{% | |
\ifcsname#1:SORT\endcsname% | |
\def\:i##1{\:ap\:tmp{##1{#2}}}% | |
\def\:tmp{\let\:tmp\empty}% | |
\csname#1:SORT\endcsname% | |
\global\let\:sorted\relax% | |
\else% | |
\def\:tmp{\:typgenwarn{sorted list "#1" does not exist. (ignored)}}% | |
\fi\:tmp} | |
\long\def\:dosort#1#2#3#4{% | |
\begingroup% | |
\def\:tmp{#2}% | |
\ifx\:tmp\:numless% | |
\let\:comp\:compnum% | |
\let\:dogreat\:doswitch% | |
\let\:doless\:donone% | |
\else\ifx\:tmp\:numgreat% | |
\let\:comp\:compnum% | |
\let\:dogreat\:donone% | |
\let\:doless\:doswitch% | |
\else\ifx\:tmp\:alphless% | |
\let\:comp\:compalpha% | |
\let\:dogreat\:doswitch% | |
\let\:doless\:donone% | |
\else\ifx\:tmp\:alphgreat% | |
\let\:comp\:compalpha% | |
\let\:dogreat\:donone% | |
\let\:doless\:doswitch% | |
\else\ifx\:tmp\:asciiless% | |
\let\:comp\:compascii% | |
\let\:dogreat\:doswitch% | |
\let\:doless\:donone% | |
\else\ifx\:tmp\:alphless% | |
\let\:comp\:compascii% | |
\let\:dogreat\:donone% | |
\let\:doless\:doswitch% | |
\fi\fi\fi\fi\fi\fi% | |
\BRANCHTYPES{#1}{\MAP{\TYPE}{% | |
\expandafter\gdef\expandafter\::tmp\expandafter{\expandafter{\ME}}% | |
\protected@edef\:tmp{#3}% %%% LaTeX's \protect mechanism | |
\global\expandafter\:ap\expandafter\::tmp\expandafter{\:tmp}% | |
\aftergroup\:makenode}}% | |
\setbox0=\vbox{% | |
\gdef\:scnt{0}#4% | |
\let\:bcnt\:scnt% | |
\:bubblesort% | |
\gdef\:sorted{}% | |
\def\:bcnt{1}% | |
\:buildsorted}% | |
\gdef\:scnt{0}% | |
\endgroup} | |
\def\:makenode{% | |
\:count\:scnt% | |
\advance\:count1% | |
\xdef\:scnt{\the\:count}% | |
\global\expandafter\let\csname\:scnt :ar\endcsname\::tmp% | |
\gdef\::tmp{}} | |
\def\:doswitch{% | |
\expandafter\let\expandafter\:tmp\csname\:fcnt :ar\endcsname% | |
\global\expandafter\expandafter\expandafter\let\expandafter\expandafter% | |
\csname\:fcnt :ar\endcsname\csname\:lcnt :ar\endcsname% | |
\global\expandafter\let\csname\:lcnt :ar\endcsname\:tmp} | |
\let\:donone\relax | |
\long\def\:checksortarg#1{% | |
\:gotrue% | |
\def\:tmp{#1}% | |
\ifx\:tmp\:numless% | |
\else\ifx\:tmp\:numgreat% | |
\else\ifx\:tmp\:alphless% | |
\else\ifx\:tmp\:alphgreat% | |
\else\ifx\:tmp\:asciiless% | |
\else\ifx\:tmp\:alphless% | |
\else% | |
\:gofalse% | |
\:typgenwarn{"#1" is not a valid sort option (ignored). Use one | |
of: \:mb \:numless, \:numgreat, \:alphless, \:alphgreat, | |
\:asciiless, \:asciigreat}% | |
\fi\fi\fi\fi\fi\fi} | |
\def\:numless{num<} | |
\def\:numgreat{num>} | |
\def\:alphless{alpha<} | |
\def\:alphgreat{alpha>} | |
\def\:asciiless{ascii<} | |
\def\:asciigreat{ascii>} | |
\def\:compnum{% | |
\ifnum\:first>\:second% | |
\let\:doresult\:dogreat% | |
\else\ifnum\:first<\:second% | |
\let\:doresult\:doless% | |
\else% | |
\let\:doresult\:donone% | |
\fi\fi} | |
\def\:compalpha{% | |
\def\:comptemp{% | |
\edef\:firstlett{\lccode\:firstlet}% | |
\edef\:secondlett{\lccode\:secondlet}% | |
\ifnum\:firstlett>\:secondlett% | |
\let\:doresult\:dogreat% | |
\else\ifnum\:firstlett<\:secondlett% | |
\let\:doresult\:doless% | |
\else% | |
\ifnum\:firstlet>\:secondlet% | |
\let\:doresult\:dogreat% | |
\else\ifnum\:firstlet<\:secondlet% | |
\let\:doresult\:doless% | |
\else% | |
\let\:doresult\:donone% | |
\let\:tmp\:compstring% | |
\fi\fi% | |
\fi\fi}% | |
\:compstring} | |
\def\:compascii{% | |
\def\:comptemp{% | |
\ifnum\:firstlet>\:secondlet% | |
\let\:doresult\:dogreat% | |
\else\ifnum\:firstlet<\:secondlet% | |
\let\:doresult\:doless% | |
\else% | |
\let\:doresult\:donone% | |
\let\:tmp\:compstring% | |
\fi\fi}% | |
\:compstring} | |
\def\:compstring{% | |
\expandafter\:suckfirst\:first\:endsuck% | |
\expandafter\:sucksecond\:second\:endsuck% | |
\let\:tmp\relax% | |
\ifx\:first\empty% | |
\ifx\:second\empty% | |
\let\:doresult\:donone% | |
\else% | |
\let\:doresult\:doless% | |
\fi% | |
\else% | |
\ifx\:second\empty% | |
\let\:doresult\:dogreat% | |
\else% | |
\edef\:firstlet{\expandafter`\:firstlet}% | |
\edef\:secondlet{\expandafter`\:secondlet}% | |
\:comptemp% | |
\fi% | |
\fi\:tmp} | |
\def\:endsuck{\:endsuck} | |
\def\:suckfirst{\futurelet\:tmp\::suckfirst} | |
\def\::suckfirst{% | |
\ifx\:tmp\:endsuck\let\:tmp\:finishfirst% | |
\else\ifx\:tmp\:sptoken\let\:tmp\:getfirstsp% | |
\else\let\:tmp\:getfirstlet% | |
\fi\fi\:tmp} | |
\def\:finishfirst#1\:endsuck{\def\:first{#1}} | |
\def\:getfirstsp#1 {\def\:firstlet{ }\:finishfirst} | |
\def\:getfirstlet#1{\def\:firstlet{#1}\:finishfirst} | |
\def\:sucksecond{\futurelet\:tmp\::sucksecond} | |
\def\::sucksecond{% | |
\ifx\:tmp\:endsuck\let\:tmp\:finishsecond% | |
\else\ifx\:tmp\:sptoken\let\:tmp\:getsecondsp% | |
\else\let\:tmp\:getsecondlet% | |
\fi\fi\:tmp} | |
\def\:finishsecond#1\:endsuck{\def\:second{#1}} | |
\def\:getsecondsp#1 {\def\:secondlet{ }\:finishsecond} | |
\def\:getsecondlet#1{\def\:secondlet{#1}\:finishsecond} | |
\def\:bubblesort{% | |
\ifnum\:bcnt>0% | |
\def\:fcnt{1}% | |
\def\:lcnt{2}% | |
\:bubbleloop% | |
\:count\:bcnt% | |
\advance\:count-1% | |
\edef\:bcnt{\the\:count}% | |
\let\:tmp\:bubblesort% | |
\else% | |
\let\:tmp\relax% | |
\fi\:tmp} | |
\long\def\:getsecond#1#2\:delim{\def\:tmp{#2}} | |
\def\:bubbleloop{% | |
\ifnum\:fcnt<\:bcnt% | |
\expandafter\let\expandafter\:first\csname\:fcnt :ar\endcsname% | |
\expandafter\let\expandafter\:second\csname\:lcnt :ar\endcsname% | |
\expandafter\:getsecond\:first\:delim% | |
\let\:first\:tmp% | |
\expandafter\:getsecond\:second\:delim% | |
\let\:second\:tmp% | |
\:comp% | |
\:doresult% | |
\let\:fcnt\:lcnt% | |
\:count\:lcnt% | |
\advance\:count1% | |
\edef\:lcnt{\the\:count}% | |
\let\:tmp\:bubbleloop% | |
\else% | |
\let\:tmp\relax% | |
\fi\:tmp} | |
\def\:buildsorted{% | |
\ifnum\:bcnt>\:scnt% | |
\let\:tmp\relax% | |
\else% | |
\expandafter\:build\csname\:bcnt :ar\endcsname% | |
\:count\:bcnt% | |
\advance\:count1% | |
\edef\:bcnt{\the\:count}% | |
\let\:tmp\:buildsorted% | |
\fi\:tmp} | |
\def\:build#1{\expandafter\:buildsuck#1\:endsuck\gdef#1{}} | |
\def\:buildsuck#1#2\:endsuck{% | |
\expandafter\gdef\expandafter\:sorted\expandafter{\:sorted\:i{#1}}% | |
} | |
%%%%% | |
%% \SIFT{Foobar}{sift}{field}{value}{Datatype macros}{OPTION} | |
%% | |
\long\def\SIFT#1#2#3#4#5#6{\:typetest{#1}{% | |
\:checksiftarg{#2}% | |
\if:go% | |
\:dosift{#1}{#3}{#4}{#5}% | |
\def\:i##1{\:ap\:tmp{##1{#6}}}% | |
\def\:tmp{\let\:tmp\empty}% | |
\:sifted% | |
\global\let\:sifted\relax% | |
\fi% | |
}\:tmp} | |
%%%%% | |
%% \MAKESIFT{Foobar}{sift}{field}{value}{Datatype macros}{NAME} | |
%% | |
\long\def\MAKESIFT#1#2#3#4#5#6{\:typetest{#1}{% | |
\:checksiftarg{#2}% | |
\if:go% | |
\ifcsname#6:SIFT\endcsname% | |
\:typgenwarn{"#6" reused as sifted list name.}% | |
\fi% | |
\:dosift{#1}{#3}{#4}{#5}% | |
\expandafter\let\csname#6:SIFT\endcsname\:sifted% | |
\global\let\:sifted\relax% | |
\fi% | |
}} | |
%%%%% | |
%% \USESIFT{NAME}{OPTION} | |
%% | |
\long\def\USESIFT#1#2{% | |
\ifcsname#1:SIFT\endcsname% | |
\def\:i##1{\:ap\:tmp{##1{#2}}}% | |
\def\:tmp{\let\:tmp\empty}% | |
\csname#1:SIFT\endcsname% | |
\global\let\:sorted\relax% | |
\else% | |
\def\:tmp{\:typgenwarn{sifted list "#1" does not exist. (ignored)}}% | |
\fi\:tmp} | |
\long\def\:checksiftarg#1{% | |
\:gotrue% | |
\def\:tmp{#1}% | |
\ifx\:tmp\:equal% | |
\def\:sift{\ifx\:first\:second\:gotrue\fi}% | |
\else\ifx\:tmp\:notequal% | |
\def\:sift{\ifx\:first\:second\else\:gotrue\fi}% | |
\else\ifx\:tmp\:numless% | |
\def\:sift{\let\:doless\:gotrue\:compnum\:doresult}% | |
\else\ifx\:tmp\:notnumless% | |
\def\:sift{\let\:donone\:gotrue\let\:dogreat\:gotrue\:compnum\:doresult}% | |
\else\ifx\:tmp\:numgreat% | |
\def\:sift{\let\:dogreat\:gotrue\:compnum\:doresult}% | |
\else\ifx\:tmp\:notnumgreat% | |
\def\:sift{\let\:donone\:gotrue\let\:doless\:gotrue\:compnum\:doresult}% | |
\else\ifx\:tmp\:alphless% | |
\def\:sift{\let\:doless\:gotrue\:compalpha\:doresult}% | |
\else\ifx\:tmp\:notalphless% | |
\def\:sift{\let\:donone\:gotrue\let\:dogreat\:gotrue\:compalpha\:doresult}% | |
\else\ifx\:tmp\:alphgreat% | |
\def\:sift{\let\:dogreat\:gotrue\:compalpha\:doresult}% | |
\else\ifx\:tmp\:notalphgreat% | |
\def\:sift{\let\:donone\:gotrue\let\:doless\:gotrue\:compalpha\:doresult}% | |
\else\ifx\:tmp\:asciiless% | |
\def\:sift{\let\:doless\:gotrue\:compascii\:doresult}% | |
\else\ifx\:tmp\:notasciiless% | |
\def\:sift{\let\:donone\:gotrue\let\:dogreat\:gotrue\:compascii\:doresult}% | |
\else\ifx\:tmp\:asciigreat% | |
\def\:sift{\let\:dogreat\:gotrue\:compascii\:doresult}% | |
\else\ifx\:tmp\:notasciigreat% | |
\def\:sift{\let\:donone\:gotrue\let\:doless\:gotrue\:compascii\:doresult}% | |
\else% | |
\:gofalse% | |
\:typgenwarn{"#1" is not a valid sift option (ignored). Use one | |
of: \:mb \:equal, \:notequal, \:numless, \:notnumless, | |
\:numgreat, \:notnumgreat, \:alphless, \:notalphless, | |
\:alphgreat, \:notalphgreat, \:mb \:asciiless, \:notasciiless, | |
\:asciigreat, \:notasciigreat}% | |
\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} | |
\def\:equal{=} | |
\def\:notequal{!=} | |
\def\:notnumless{num<=} | |
\def\:notnumgreat{num>=} | |
\def\:notalphless{alpha<=} | |
\def\:notalphgreat{alpha>=} | |
\def\:notasciiless{ascii<=} | |
\def\:notasciigreat{ascii>=} | |
\long\def\:dosift#1#2#3#4{% | |
\begingroup% | |
\BRANCHTYPES{#1}{\MAP{\TYPE}{% | |
\protected@edef\:first{#2}% %%% LaTeX's \protect mechanism | |
\protected@edef\:second{#3}% %%% LaTeX's \protect mechanism | |
\let\:donone\relax% | |
\let\:dogreat\relax% | |
\let\:doless\relax% | |
\:gofalse% | |
\:sift% | |
\if:go% | |
\expandafter\:addsift\ME\:endsuck% | |
\fi}}% | |
\gdef\:sifted{}% | |
\setbox0=\vbox{#4}% | |
\endgroup} | |
\long\def\:addsift#1\:endsuck{% | |
\:singletst#1\:delim% | |
\ifx\:tmp\empty% | |
\expandafter\gdef\expandafter\:sifted\expandafter{\:sifted\:i#1}% | |
\else% | |
\expandafter\gdef\expandafter\:sifted\expandafter{\:sifted\:i{#1}}% | |
\fi} | |
%%%%% | |
%% reserving TeX registers | |
\newcount\:count | |
\newtoks\:toks | |
%%%%% | |
%% formatting of error and warning messages | |
\def\:mb{^^J\space\space} %% linebreak in error/warning text | |
\def\:err{\errmessage{datatype error}} | |
\def\:typerr#1#2{\immediate\write16{% | |
^^J! datatype \MYTYPESTRING\space error: #1% | |
^^J? #2% | |
^^J!punting LaTeX!}% | |
\batchmode\@@end} | |
\def\:typgenerr#1#2{\scrollmode\:err\immediate\write16{% | |
^^J! datatype error: #1% | |
^^J? #2% | |
^^J!punting LaTeX!}% | |
\batchmode\@@end} | |
\def\:typwarn#1{\immediate\write16{% | |
^^J! datatype \MYTYPESTRING\space warning: #1% | |
^^J}} | |
\def\:typgenwarn#1{\immediate\write16{% | |
^^J! datatype warning: #1% | |
^^J}} | |
%%%%% | |
%% some "standard" errors | |
\def\er:tblank{\:typgenerr{There is no "" (empty string) datatype.} | |
{A string of some sort is expected. Don't leave the typestring | |
for\:mb a datatype blank.}} | |
\def\er:tunknown#1{\:typgenerr{Datatype #1 isn't declared.} | |
{There is no datatype #1 declared. Use \string\DECLARETYPE{#1} | |
\:mb(or \string\DECLARESUBTYPE) to declare it.}} | |
\def\er:funset#1{\:typerr{\string#1 used but not \noexpand\s in | |
\expandafter\string\MYCOMMAND.}{\string#1 must either be set by | |
\noexpand\s or have a default declared by \:mb "\noexpand\FD | |
{\string#1} { default }" before it is used.}} | |
%%%%% | |
%% handling of null cases, "reserved" commmand names, etc. | |
\gdef\:lvl{0} | |
\newif\if:go | |
\:gofalse | |
\gdef\:d{} | |
\gdef\:t{} | |
\gdef\:p{} | |
\gdef\:ct{} | |
\gdef\:ca#1{} | |
\gdef\:pdo{} | |
\gdef\:phd{} | |
\gdef\:phh{} | |
\gdef\:pre{} | |
\gdef\:pst{} | |
\gdef\:do{} | |
\gdef\:hd{} | |
\gdef\:hh{} | |
\gdef\:rdo{} | |
\gdef\:rhd{} | |
\gdef\:rhh{} | |
\gdef\:lst{} | |
\gdef\:add#1{} | |
\def\:nocontext#1{\def#1{\:typgenerr{Don't use \string#1 outside the | |
scope of a DataType.}{It has no context.}}} | |
\:nocontext\MYCOMMAND | |
\:nocontext\ME | |
\:nocontext\MYTYPESTRING | |
\:nocontext\MYPARENTSTRING | |
\:nocontext\MYMAP | |
\:nocontext\PARENT | |
\:nocontext\PARENTMAP | |
\:nocontext\PARENTSURFACEMAP | |
\:nocontext\ADD | |
%\:nocontext\RESET | |
\:nocontext\F | |
\:nocontext\FD | |
\:nocontext\FS | |
\:nocontext\s | |
\:nocontext\rs | |
\:nocontext\append | |
\:nocontext\prepend | |
%%%%% | |
%% Returning ":" to its default behavior, so it can no longer be | |
%% used in TeX command names. | |
\catcode`\:12\relax | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment