% This is the YaX package. % Relevant information can be found in yax-doc.pdf % % Author: Paul Isambert. % E-mail: zappathustra AT free DOT fr % Comments and suggestions are welcome. % Date: July 2010. % \input texapi % Everything I need. It figures: I wrote it for this. \ifcsname yax@already_loaded\endcsname \expandafter\endinput \fi % No need to test texapi's version, since \texapiversion was % defined in v.1.01 and that's the one needed. \unless\ifdefined\texapiversion \senderror{YaX}{You need texapi with version at least 1.01. I quit} \expandafter\endinput \fi \setcatcodes{@_=11} \def\yaxversion{1.01} \let\yax@already_loaded\relax \def\yax@error{\senderror{YaX}} % % Retrieval of parameter and attribute. % \def\yax@getparameter#1#2:{% \passtrim{#2}{#1}% } \def\yax@defparameter#1#2{% \def\yax@parameter{#2}#1 } % #1 and #2 are thing_to_do and parameter. \def\yax@getattribute#1#2#3 {% \ifemptystring{#3}{\yax@getattribute{#1}{#2}} {#1{#2}{#3}}% } % \yax@getall\foo % => \foo{}{} \def\yax@getall#1{% \yax@getparameter{\yax@getattribute{#1}}% } % \long\def\yax@get_topar#1#2\par{% #1{#2}% } % % % % % SETTING PARAMETERS % % The crux of the biscuit. % % \setparameter : % = % = % ... % = % \par % % A generic loop for space-delimited lists. % The the texapi documentation for the information on the \newfor loop. % \newfornoempty\yax@space_loop{1}#2 {#1{#2}}% % \pdef\setparameter{% \yax@getparameter\yax@setparameter_getattributes } \long\def\yax@setparameter_getattributes#1#2\par{% \yax@space_loop{\yax@setparameter{#2}}{#1 }% } \long\def\yax@setparameter#1#2{% \deftrim\yax@parameter{#2}% \yax@setattributes_loop{#1 }% The space at the end delimits the last value if needed. } \newfor\yax@setattributes_loop#1={% \passtrim{#1}\yax@setattributes_checkprefix \addright\yax@attribute{ }% Final space delimiter \pausefor\yax@setattributes_howgiven }[\iffcs{yax@\yax@parameter @active} {\usecs{yax@\yax@parameter @def}}] \def\yax@setattributes_checkprefix#1{% \ifprefix{e:}{#1} {\let\yax@dodef\edefcs \removeprefixand{e:}{#1}{\deftrim\yax@attribute}} {\ifprefix{g:}{#1} {\let\yax@dodef\gdefcs \removeprefixand{g:}{#1}{\deftrim\yax@attribute}} {\ifprefix{x:}{#1} {\let\yax@dodef\xdefcs \removeprefixand{x:}{#1}{\deftrim\yax@attribute}} {\let\yax@dodef\defcs \def\yax@attribute{#1}}}}% } \def\yax@setattributes_howgiven{% \ifnextnospace" \yax@setattributes_getquote % No need for a new nospace: it has been removed {\ifcatnext\foo \yax@setattributes_getcommand \yax@setattributes_getspace}% } \long\def\yax@setattribute_single#1#2{% \def\yax@attribute{#2}% \yax@setattributes_do{#1}% } \long\def\yax@setattributes_getquote"#1"{% \passexpanded{\yax@space_loop{\yax@setattribute_single{#1}}}{\yax@attribute}% \nospace{\resumefor\yax@setattributes_loop}% Better not to forget this! } \long\def\yax@setattributes_getcommand#1{% \passexpanded{\yax@space_loop{\yax@setattribute_single{#1}}}{\yax@attribute}% \nospace{\resumefor\yax@setattributes_loop}% } \long\def\yax@setattributes_getspace#1 {% \passexpanded{\yax@space_loop{\yax@setattribute_single{#1}}}{\yax@attribute}% \nospace{\resumefor\yax@setattributes_loop}% } \newif\ifyax@makedef \long\def\yax@setattributes_do#1{% \yax@makedeftrue \iffcs{yax@\yax@parameter @restricted}% Parameter restricted. {\reverse\iffcs{yax@\yax@parameter:\yax@attribute @allowed}% Attribute not allowed. {\yax@makedeffalse \yax@error{Attribute `\unexpandedcs{yax@attribute}' is not allowed in parameter `\yax@parameter'. Authorized attributes are `\unexpandedcs{yax@\yax@parameter @restricted}'}}}% \ifcs{yax@\yax@parameter:\yax@attribute @restricted}% Attribute restricted in this parameter. {\reverse\iffcs{yax@\yax@parameter:\yax@attribute:\detokenize{#1}@allowed}% Value not allowed. {\yax@makedeffalse \yax@error{Value `\detokenize{#1}' is not allowed in attribute `\yax@parameter:\yax@attribute'. Authorized values are `\unexpandedcs{yax@\yax@parameter:\yax@attribute @restricted}'}}}% {\iffcs{yax@attribute@\yax@attribute @restricted}% Attribute restricted in all parameters. {\reverse\iffcs{yax@attribute@\yax@attribute:\detokenize{#1}@allowed}% Value not allowed. {\yax@makedeffalse \yax@error{Value `\detokenize{#1}' is not allowed in attribute `\yax@attribute'. Authorized values are `\unexpandedcs{yax@attribute@\yax@attribute @restricted}'}}}}% \ifyax@makedef \yax@dodef{yax@\yax@parameter:\yax@attribute}{#1}% \fi } % % % % % \restrictparameter : % % % ... % % \par % \pdef\restrictparameter{% \yax@getparameter\yax@restrictparameter_getrestrictions }% \long\def\yax@restrictparameter_getrestrictions#1#2\par{% \yax@space_loop{\yax@restrictparameter{#2 meta }}{#1 }% Meta is always allowed. } \long\def\yax@restrictparameter#1#2{% \def\yax@parameter{#2}% \yax@space_loop\yax@restrictparameter_readrestrictions{#1}% } \long\def\yax@restrictparameter_readrestrictions#1{% \letcs{yax@\yax@parameter :#1@allowed}\relax \ifcs{yax@\yax@parameter @restricted} {\addrightcs{yax@\yax@parameter @restricted}{, #1}} {\defcs{yax@\yax@parameter @restricted}{#1}}% } % % \restrictattribute : % % % ... % %\par % \pdef\restrictattribute{% \yax@getall\yax@restrictattribute_getrestrictions } \long\def\yax@restrictattribute_getrestrictions#1#2#3\par{% \yax@space_loop{\yax@restrictattribute_readrestrictions{#1}{#2}}{#3 }% } \long\def\yax@restrictattribute_readrestrictions#1#2#3{% \letcs{yax@#1:#2:\detokenize{#3}@allowed}\relax \ifcs{yax@#1:#2@restricted} {\addrightcs{yax@#1:#2@restricted}{, #3}} {\defcs{yax@#1:#2@restricted}{#3}}% } % % \restrictallattributes % % % ... % %\par % \long\pdef\restrictallattributes#1 #2\par{% \yax@space_loop{\yax@restrictallattributes_readrestrictions{#1}}{#2 }% } \long\def\yax@restrictallattributes_readrestrictions#1#2{% \letcs{yax@attribute@#1:\detokenize{#2}@allowed}\relax \ifcs{yax@attribute@#1@restricted} {\addrightcs{yax@attribute@#1@restricted}{, #2}} {\defcs{yax@attribute@#1@restricted}{#2}}% } % % % % % \defparameter{} % \defactiveparameter{} % \executeparameter : % \newif\ifyax@activeparameter \def\defparameter#1#{% \def\yax@parameter{#1 }% \afterassignment\yax@defparameter_do \def\yax@temp##1% }% \def\yax@defparameter_do{% \passexpanded{\yax@space_loop\yax@defparameter_def}\yax@parameter } \def\defactiveparameter{% \yax@activeparametertrue\defparameter }% \long\def\yax@defparameter_def#1{% \edefcs{yax@#1@def}{\unexpanded\expandafter{\yax@temp{#1}}}% \ifyax@activeparameter \yax@activeparameterfalse \letcs{yax@#1@active}\relax \fi } \def\executeparameter{% \yax@getparameter\yax@executeparameter } \def\yax@executeparameter#1{% \iffcs{yax@#1@def}{\csname yax@#1@def\endcsname}% } % % % % % \deleteattribute : % \def\deleteattribute{% \yax@getall\yax@deleteattribute } \def\yax@deleteattribute#1#2{% \letcs{yax@#1:#2}\yax@undefined } % % \setattribute : = % \def\yax@removesign#1#2#3#4={#1{#2}{#3}}% \def\setattribute{% \let\yax@dodef\defcs \yax@getall{\yax@removesign\yax@setattribute}% } \def\esetattribute{% \let\yax@dodef\edefcs \yax@getall{\yax@removesign\yax@setattribute}% } \def\gsetattribute{% \let\yax@dodef\gdefcs \yax@getall{\yax@removesign\yax@setattribute}% } \def\xsetattribute{% \let\yax@dodef\xdefcs \yax@getall{\yax@removesign\yax@setattribute}% } \def\yax@setattribute#1#2{% \def\yax@parameter{#1}% \def\yax@attribute{#2}% \nospace\yax@setattribute_getvalue } \def\yax@setattribute_getvalue#1 {% \yax@setattributes_do{#1}% } % % % % % USING VALUES % % These are the loops used to check whether : exists, % and if not to search the meta-parameters. They are used of course in % \usevalue and associates. % I could use one loop only for all macros, but then I should have used more % internal code to pass arguments here and there. % % #1 = list of parameters already checked (used to spot loops in meta-search) % => becomes {#1{#3}} on each iteration. % #2 = command executed on yax@: if it exists. % => doesn't change from one iteration to the next. % #3 = % => becomes \usecs{yax@:} on each iteration, thus % returning a new to be searched. % #4 = % => doesn't change from one iteration to the next. % #5 = if alone, when #6 exists. % #6 = . % => neither change. % % The loop does the following: % If : exists, then execute #2 on it followed by if any. % Or, if has a meta attribute, it calls % the \yax@ifattribute_checkmeta subloop with and % as passed arguments and as the list argumen. % This command compare each item in the list with , and if they % have the same meta, it means we're in a meta-loop, we break and execute . % If no match obtains, we rerun the original loop (with #3 turned to its meta). % Or, if had no meta in the first place, we break and execute . % \newwhile\yax@ifattribute_noarg4{#1{#3}}{#2}{\usecs{yax@#3:meta}}{#4}{% \ifcs{yax@#3:#4} {\breakwhile{#2{yax@#3:#4}}} {\ifcs{yax@#3:meta}% has no . {\yax@ifattribute_checkmeta{}{#3}{#1}}% It has a meta, though. We do some testing (see next macro). {\breakwhile{}}}% No meta, we break the loop, gobble and release } \newwhile\yax@ifattribute_or5{#1{#3}}{#2}{\usecs{yax@#3:meta}}{#4}{#5}{% \ifcs{yax@#3:#4} {\breakwhile{#2{yax@#3:#4}}} {\ifcs{yax@#3:meta} {\yax@ifattribute_checkmeta{#5}{#3}{#1}} {\breakwhile{#5}}}% } \newwhile\yax@ifattribute_and6{#1{#3}}{#2}{\usecs{yax@#3:meta}}{#4}{#5}{#6}{% \ifcs{yax@#3:#4} {\breakwhile{#2{yax@#3:#4}#5}} {\ifcs{yax@#3:meta} {\yax@ifattribute_checkmeta{#6}{#3}{#1}} {\breakwhile{#6}}}% } % The _if version pass to the execution of the parameter, % since a test is supposed to happen. \newwhile\yax@ifattribute_if6{#1{#3}}{#2}{\usecs{yax@#3:meta}}{#4}{#5}{#6}{% \ifcs{yax@#3:#4} {\breakwhile{#2{yax@#3:#4}{#5}{#6}}} {\ifcs{yax@#3:meta} {\yax@ifattribute_checkmeta{#6}{#3}{#1}} {\breakwhile{#6}}}% } \newfor\yax@ifattribute_checkmeta{2}#3{% \iffxcscs{yax@#2:meta}{yax@#3:meta}% Both parameters have the same meta. So we're in a meta-loop, and we break. {\breakfor{\breakwhile{#1}}}% } % % The nometa version are definitely simpler. % \long\def\yax@ifattributenometa_and#1#2#3#4#5{% \ifcs{yax@#2:#3}{#1{yax@#2:#3}#4}{#5}% } \long\def\yax@ifattributenometa_or#1#2#3#4{% \ifcs{yax@#2:#3}{#1{yax@#2:#3}}{#4}% } \def\yax@ifattributenometa_noarg#1#2#3{% \iffcs{yax@#2:#3}{#1{yax@#2:#3}}% } % % % % % \nometa % \def\nometa#1{% \ifcs{yax@\commandtoname#1_nometa} {\usecs{yax@\commandtoname#1_nometa}} {\yax@error{You can't use \noexpand\nometa before \string#1}}% } % % % % Defining \, \or and \and at once, % with the \nometa-variant. % \def\yax@def_noarg#1#2{% \def#1{\yax@getall{\yax@ifattribute_noarg{}{#2}}}% \defcs{\commandtoname#1or}{\yax@getall{\yax@ifattribute_or{}{#2}}}% \defcs{\commandtoname#1and}{\yax@getall{\yax@ifattribute_and{}{#2}}}% % \defcs{yax@\commandtoname#1_nometa}{\yax@getall{\yax@ifattributenometa_noarg{#2}}}% \defcs{yax@\commandtoname#1or_nometa}{\yax@getall{\yax@ifattributenometa_or{#2}}}% \defcs{yax@\commandtoname#1and_nometa}{\yax@getall{\yax@ifattributenometa_and{#2}}}% } \def\yax@def_onearg#1#2{% \def#1##1{\yax@getall{\yax@ifattribute_noarg{}{#2{##1}}}}% \defcs{\commandtoname#1or}##1{\yax@getall{\yax@ifattribute_or{}{#2{##1}}}}% \defcs{\commandtoname#1and}##1{\yax@getall{\yax@ifattribute_and{}{#2{##1}}}}% % \defcs{yax@\commandtoname#1_nometa}##1{\yax@getall{\yax@ifattributenometa_noarg{#2{##1}}}}% \defcs{yax@\commandtoname#1or_nometa}##1{\yax@getall{\yax@ifattributenometa_or{#2{##1}}}}% \defcs{yax@\commandtoname#1and_nometa}##1{\yax@getall{\yax@ifattributenometa_and{#2{##1}}}}% } % % % % % \ifattribute % \def\ifattribute{% \yax@getall{\yax@ifattribute_and{}\gobbleone}% }% \def\yax@ifattribute_nometa{% \yax@getall{\yax@ifattributenometa_and\gobbleone}% } % % % % % \passvalue % \yax@def_onearg\passvalue\yax@passvalue \def\yax@passvalue#1#2{% \passexpandedcs{#1}{#2}% } \yax@def_onearg\passvaluenobraces\yax@passvalue_nobrace \def\yax@passvalue_nobrace#1#2{% \passexpandedcs\swapargs{#2}{#1}% } % % % % % \usevalue % \yax@def_noarg\usevalue\usecs % % % % % \settovalue (not expandable, obviously, % although it leaves nothing in the input but % the assignment. % \yax@def_onearg\settovalue\yax@settovalue \def\yax@settovalue#1#2{% #1 \usecs{#2}\relax } % % % % % \storevalue (like \settovalue, basically). % \yax@def_onearg\storevalue\yax@storevalue \def\yax@storevalue#1#2{% \lettocs#1{#2}% }% % % % % % \ifvalue % \def\ifvalue{% \yax@getall{\yax@removesign\yax@ifvalue_getvalue}% }% \def\yax@ifvalue_nometa{% \yax@getall{\yax@removesign\yax@ifvaluenometa_getvalue}% }% \long\def\yax@ifvalue_getvalue#1#2#3 {% \ifemptystring{#3} {\yax@ifvalue_getvalue{#1}{#2}} {\yax@ifattribute_if{}{\passexpandedcs{\ifstring{#3}}}{#1}{#2}}% } \def\yax@ifvaluenometa_getvalue#1#2#3 {% \ifemptystring{#3} {\yax@ifvaluenometa_getvalue{#1}{#2}} {\ifcs{yax@#1:#2}{\passexpandedcs{\ifstring{#3}}{yax@#1:#2}}\secondoftwo}% } % % % % % \ifcasevalue % \let\yax@ifcasevalue_passvalueor\passvalueor % just in case \passvalue is redefined by a \newsyntax. \let\yax@yax@ifcasevalue_passvalueor_nometa\yax@passvalueor_nometa \letcstocs{yax@yax@ifcasevalue_passvalueor_nometa}{yax@passvalueor_nometa} \def\ifcasevalue{% \yax@getall\yax@ifcasevalue_getvalue } \def\yax@ifcasevalue_nometa{% \yax@getall{\expandafter\nometa\yax@ifcasevalue_getvalue}% } \def\yax@ifcasevalue_getvalue#1#2{% \yax@ifcasevalue_passvalueor\yax@ifcasevalue_scan#1:#2 \yax@ifcasevalue_novalue } \newstring\val \newstring\elseval \long\def\yax@ifcasevalue_novalue#1\endval{% \iffcontains{\elseval}{#1}{\splitstring{\elseval}{#1}\yax@ifcasevalue_doelse}% } \def\yax@ifcasevalue_doelse#1#2{\trim{#2}} \def\yax@ifcasevalue_scan#1#2\endval{% \ifcontains{\elseval}{#2} {\splitstring{\elseval}{#2}\yax@ifcasevalue_dotest{#1}} {\yax@ifcasevalue_dotest{#2}{}{#1}}% } \def\yax@ifcasevalue_dotest#1#2#3{% \yax@ifcasevalue_loop{#3}{#2}{{yax@dummy_value} #1\val}% } \newfor\yax@ifcasevalue_loop{2}#3 #4\val{% \iffstring{#1}{#3}{\passtrim{#4}\breakfor}% }[\passtrim{#2}\unbrace] % % % % % \newsyntax % \long\def\yax@newsyntax_noarg#1#2#3{% \defcs{#1}#2{\yax@ifattribute_noarg{}{#3}{##1}{##2}}% \defcs{#1or}#2{\yax@ifattribute_or{}{#3}{##1}{##2}}% \defcs{#1and}#2{\yax@ifattribute_and{}{#3}{##1}{##2}}% % \defcs{yax@#1_nometa}#2{\yax@ifattributenometa_noarg{#3}{##1}{##2}}% \defcs{yax@#1or_nometa}#2{\yax@ifattributenometa_or{#3}{##1}{##2}}% \defcs{yax@#1and_nometa}#2{\yax@ifattributenometa_and{#3}{##1}{##2}}% } \long\def\yax@newsyntax_onearg#1#2#3{% \defcs{#1}##1#2{\yax@ifattribute_noarg{}{#3{##1}}{##2}{##3}}% \defcs{#1or}##1#2{\yax@ifattribute_or{}{#3{##1}}{##2}{##3}}% \defcs{#1and}##1#2{\yax@ifattribute_and{}{#3{##1}}{##2}{##3}}% % \defcs{yax@#1_nometa}##1#2{\yax@ifattributenometa_noarg{#3{##1}}{##2}{##3}}% \defcs{yax@#1or_nometa}##1#2{\yax@ifattributenometa_or{#3{##1}}{##2}{##3}}% \defcs{yax@#1and_nometa}##1#2{\yax@ifattributenometa_and{#3{##1}}{##2}{##3}}% } \long\def\newsyntax#1#{% \yax@newsyntax{#1}% } \long\def\yax@newsyntax#1#2{% \defcs{#2restrictattribute}#1{\yax@restrictattribute_getrestrictions{##1}{##2}}% \defcs{#2deleteattribute}#1{\letcs{yax@##1:##2}\yax@undefined}% \defcs{#2setattribute}#1{\let\yax@dodef\defcs\yax@removesign{\nospace\yax@setattribute}{##1}{##2}}% \defcs{#2esetattribute}#1{\let\yax@dodef\edefcs\yax@removesign{\nospace\yax@setattribute}{##1}{##2}}% \defcs{#2gsetattribute}#1{\let\yax@dodef\gdefcs\yax@removesign{\nospace\yax@setattribute}{##1}{##2}}% \defcs{#2xsetattribute}#1{\let\yax@dodef\xdefcs\yax@removesign{\nospace\yax@setattribute}{##1}{##2}}% \defcs{#2ifattribute}#1{\yax@ifattribute_and{}\gobbleone{##1}{##2}}% \defcs{yax@#2ifattribute_nometa}#1{\yax@ifattributenometa_and\gobbleone{##1}{##2}}% \yax@newsyntax_noarg{#2usevalue}{#1}\usecs \def\yax@temp##1##2{#1}% \passexpanded{\yax@newsyntax_onearg{#2passvalue}}{\yax@temp{##2}{##3}}\yax@passvalue \passexpanded{\yax@newsyntax_onearg{#2passvaluenobraces}}{\yax@temp{##2}{##3}}\yax@passvalue_nobrace \passexpanded{\yax@newsyntax_onearg{#2settovalue}}{\yax@temp{##2}{##3}}\yax@settovalue \passexpanded{\yax@newsyntax_onearg{#2storevalue}}{\yax@temp{##2}{##3}}\yax@storevalue \defcs{#2ifvalue}#1{\yax@removesign\yax@ifvalue_getvalue{##1}{##2}}% \defcs{yax@#2ifvalue_nometa}#1{\yax@removesign\yax@ifvaluenometa_getvalue{##1}{##2}}% \defcs{#2ifcasevalue}#1{\yax@ifcasevalue_getvalue{##1}{##2}}% \defcs{yax@#2ifcasevalue_nometa}#1{\expandafter\nometa\yax@ifcasevalue_getvalue{##1}{##2}}% } \def\yax@let#1#2#3{% \letcstocs{#2#1}{#3#1}% \letcstocs{yax@#2#1_nometa}{yax@#3#1_nometa}% } \def\yax@let_all#1#2#3{% \yax@let{#1}{#2}{#3}\yax@let{#1or}{#2}{#3}\yax@let{#1and}{#2}{#3}% } \def\copysyntax#1#2{% \letcstocs{#1restrictattribute}{#2restrictattribute}% \letcstocs{#1deleteattribute}{#2deleteattribute}% \letcstocs{#1setattribute}{#2setattribute}% \letcstocs{#1esetattribute}{#2esetattribute}% \letcstocs{#1gsetattribute}{#2gsetattribute}% \letcstocs{#1xsetattribute}{#2xsetattribute}% \yax@let{ifattribute}{#1}{#2}% \yax@let_all{usevalue}{#1}{#2}\yax@let_all{passvalue}{#1}{#2}\yax@let_all{passvaluenobraces}{#1}{#2}% \yax@let_all{settovalue}{#1}{#2}\yax@let_all{storevalue}{#1}{#2}% \yax@let{ifvalue}{#1}{#2}% \yax@let{ifcasevalue}{#1}{#2}% } \def\letyaxcommand#1#2{% \let#1#2% Base \let. \ifcs{yax@\commandtoname#2and_nometa} % #2 is a YaX command with -and -or variants. {\letcstocs{\commandtoname#1or}{\commandtoname#2or}% \letcstocs{\commandtoname#1and}{\commandtoname#2and}% \letcstocs{yax@\commandtoname#1_nometa}{yax@\commandtoname#2_nometa}% \letcstocs{yax@\commandtoname#1or_nometa}{yax@\commandtoname#2or_nometa}% \letcstocs{yax@\commandtoname#1and_nometa}{yax@\commandtoname#2and_nometa}}% {\iffcs{yax@\commandtoname#2_nometa}% #2 is a YaX command without variants but with \nometa (i.e. the \if...). {\letcstocs{yax@\commandtoname#1_nometa}{yax@\commandtoname#2_nometa}}}% } \restorecatcodes \endinput