diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/generic/yax |
Initial commit
Diffstat (limited to 'macros/generic/yax')
-rw-r--r-- | macros/generic/yax/README | 26 | ||||
-rw-r--r-- | macros/generic/yax/t-yax.tex | 24 | ||||
-rw-r--r-- | macros/generic/yax/yax-doc.pdf | bin | 0 -> 113177 bytes | |||
-rw-r--r-- | macros/generic/yax/yax-doc.tex | 1032 | ||||
-rw-r--r-- | macros/generic/yax/yax.sty | 14 | ||||
-rw-r--r-- | macros/generic/yax/yax.tex | 707 |
6 files changed, 1803 insertions, 0 deletions
diff --git a/macros/generic/yax/README b/macros/generic/yax/README new file mode 100644 index 0000000000..681393c849 --- /dev/null +++ b/macros/generic/yax/README @@ -0,0 +1,26 @@ +This is the README file for the YaX package. +Author: Paul Isambert. +E-mail: zappathustra AT free DOT fr +Comments and suggestions are welcome. +Date: January 2011. + +The package is a format-independent key system, +with some peculiarities. +It is format-independent and based on texapi +(and thus requires e-TeX at least). +Relevant information can be found in yax-doc.pdf + +Files in this distributions: + +yax.tex => The bulk of the code. +t-yax.tex => A wrapper file for ConTeXt. +yax.sty => A wrapper file for LaTeX. +yax-doc.tex => The source of the documentation. +yax-doc.pdf => The typeset documentation. + +To process the documentation source, you need the +piTeX files (available on CTAN) on top of plain TeX +with LuaTeX at least v.0.6... except the current version +of PiTeX on CTAN is too old. + +Licensing of this package is covered by LPPL. diff --git a/macros/generic/yax/t-yax.tex b/macros/generic/yax/t-yax.tex new file mode 100644 index 0000000000..a85981e3c6 --- /dev/null +++ b/macros/generic/yax/t-yax.tex @@ -0,0 +1,24 @@ +% This is t-yax.tex, a third-party file to load +% the YaX package in ConTeXt. Useful information +% can be found in yax-doc.pdf +% +% Author: Paul Isambert. +% E-mail: zappathustra AT free DOT fr +% Comments and suggestions are welcome. +% Date: January 2011. +% +%D \module +%D [ file=t-yax, +%D version=2011.01.22, +%D title=\CONTEXT\ User Module, +%D subtitle=YaX, +%D author=Paul Isambert, +%D date=\currentdate, +%D copyright=Paul Isambert, +%D email=zappathustra@free.fr, +%D license=LaTeX Project Public License] + +\writestatus{loading}{ConTeXt User Module / yax} +\chardef\formatnumber=3 +\input yax.tex +\endinput diff --git a/macros/generic/yax/yax-doc.pdf b/macros/generic/yax/yax-doc.pdf Binary files differnew file mode 100644 index 0000000000..f29485fc9a --- /dev/null +++ b/macros/generic/yax/yax-doc.pdf diff --git a/macros/generic/yax/yax-doc.tex b/macros/generic/yax/yax-doc.tex new file mode 100644 index 0000000000..01a0fd2604 --- /dev/null +++ b/macros/generic/yax/yax-doc.tex @@ -0,0 +1,1032 @@ +\input pitex % which \input's YaX + +\setparameter document: + author = "Paul Isambert" + title = \yax + pdftitle = YaX + version = \yaxversion + date = "\the\month/\the\day/\the\year" + display = outlines + subject = "The YaX documentation." + +\setparameter navigator: + mode = outlines + +\setparameter page: + left = 5cm + right = 3cm + height = 26cm + +\setparameter font : + command = \mainfont + name = ChaparralPro- + roman = Regular + italic = Italic + bold = Bold + +\setparameter font : + command = \sectionfont + size = 13pt + name = ChaparralPro- + roman = Regular + italic = Italic + +\setparameter font : + command = \titlefont + size = 26pt + name = ChaparralPro- + roman = Regular + +\setparameter font: + command = \codefont + name = lucon + size = 8.1pt + type = ttf + roman = {} + features = "\usevalue metafont:features -trep;" + +\setparameter section: + font = \sectionfont + number = none + color = ".8 0 0" + +\setparameter sectionbookmark: + meta = navigator + up = 15pt + + +\pdfdef\ital#1{#1} + + +\def\yax{% + Y\kern-.1em \raise.15em\hbox{\sc a}\kern-.1em \lower.15em\hbox{X}% + \ifcatnext a { }{\iffnext({ }}% + } +\def\defarg#1#2{% + \def#1{\arg{#2}\kern1pt\antigobblespace}% + } +\defarg\param{parameter} \defarg\paralist{list of parameters} +\defarg\attr{attribute} \defarg\attrlist{list of attributes} +\def\paramatt{\param\kern-1pt\tcode:\attr\antigobblespace} +\defarg\attrlist{list of attributes} \defarg\vallist{list of values} +\defarg\value{value} \defarg\ospace{optional space} +\defarg\andvalue{value exists} \defarg\orvalue{no value} +\defarg\true{true} \defarg\false{false} +\defarg\codarg{code} \defarg\comarg{command} +\newif\ifdescribebookmark +\def\describe{% + \ifnext*{\describebookmarktrue\gobbleoneand\Describe} + {\describebookmarkfalse\Describe}% + } +\def\Describe{% + \ifnext!{\gobbleoneand{\let\COM\ecom\DoDescribe}} + {\let\COM\com\DoDescribe}% + } +\def\ecom#1{{\mainfont\char"E01F}\com{#1}} +\long\def\DoDescribe#1#2.{% + \ifvmode \vskip\baselineskip \fi + \noindent + \ifdescribebookmark + \outline[color = .4 .4 .4]{3}{\noexpand#1}% + \fi + \llap{\color{.8 0 0}{\COM#1}\iffemptystring{#2}{\kern1em}}#2% + \reverse\iffemptystring{#2}{\par\noindent}\ignorespaces + } + +\newblock*\syntax{\vskip\baselineskip\codefont\setcatcodes{\^^M=13}\obeylines} + {\vskip\baselineskip} +\extraboxspace=10pt + +\newbox\codebox +\newbox\processbox +\newdimen\prevcodedepth +\def\processexample{% + \setbox\codebox=\vtop{% + \let\exampleskip\relax + \parindent0pt + \hsize.75\hsize + \typesetexample + \par + \global\prevcodedepth=\prevdepth + }% + \setbox\processbox=\vtop{% + \parindent0pt + \hsize.5\hsize + \doexample + }% + \exampleskip\exampleskip + \moveleft .25\hsize\hbox{\colorbox{.9 .9 .9}{\copy\codebox\copy\processbox}}% + \exampleskip\exampleskip + \prevdepth=\prevcodedepth + } + + +%\output{% +% \shipout\vbox to\dimexpr(\vsize+3\baselineskip){% +% \box255 +% \vfil +% \hbox to\hsize{\mainfont\hfil\the\pageno\hfil}% +% } +% \advancepageno +% } +% + +\gates def {headers}{% + \setbox255=\vbox to\dimexpr(\outputsize+3\baselineskip){% + \box255 + \vfil + \hbox to\hsize{\mainfont\hfil\the\pageno\hfil}% + }% + } + +\gates def {ship}{% + \shipout\box255 + } + +\vbox to 2\baselineskip{% + \vfil + \hbox to\hsize{\hfil\titlefont \usevalue document : title } + \vfil} + +\vskip\baselineskip +\it +\hfill \usevalue document : author \par +\hfill \usevalue document : date \par +\hfill v.\usevalue document : version \par + +\rm + +\vskip2\baselineskip + + +\noindent\yax is Yet Another Key System, or \yax Ain't Keys, however you want it. +It has some peculiarities. First, keys are not keys, but attributes, +which means they depend on a parameter. You don't set key individually +in \yax (although you can do it), you define parameters, which have +attributes, which have values. Second, parameters can have definitions, +so that when you set it you can also execute a command, or execute it +later; thus, \yax is halfway between key management and macro definition. +Finally, the syntax of \yax is not traditional: there are as few braces +as possible, which you can find tremendously annoying, but it can be +changed. + +And, of course, \yax is format-independant. Files are provided for +La\TeX\ and Con\TeX t, so you can input it with \verb/\usepackage{yax}/ +or \verb/\usemodule[yax]/ respectively; anywhere else just use \verb/\input yax/. + + + +\describe!\yaxversion. +Starting with version 1.01, this macro holds \yax's version. Current is \yaxversion. + + + + +\section{Setting parameters} + +\noindent Here's an example of how values are set: + +\example +\setparameter Zappa : + firstname = Frank + motto = "Music is the best" + hairstyle = \moustache +\example/ + +\noindent Here we have defined a \ital{parameter}, `\verb/Zappa/', which +has \ital{attributes} `\verb/firstname/', `\verb/motto/' and `\verb/hairstyle/' +with \ital{values} `\verb/Frank/', `\verb/Music is the best/' and `\verb/\moustache/' +respectively. + +\describe*\setparameter. +Here's how you set parameters more precisely. (This command is not the only way to set values. +See below.) +Its simplified syntax is as follows: + +\syntax +\noindent\com\setparameter \paralist\ : + \attrlist\ = \value + \attrlist\ = \value + ... +\noindent\com\par +\syntax/ + +\noindent First, the \com\par command that terminates the declaration: +it was not chosen simply because it echoes \ital{parameter}, but above +all because it can be implicit in a blank line, i.e. \com\setparameter +can be delimited by a blank line. That's why in the first example there +seemed to be nothing delimitating \com\setparameter: it is supposed to +be followed by a blank line. I will regularly display parameter setting in this +fashion, even though the following would be equally legitimate: + +\example +\setparameter Zappa : firstname = Frank ... \par +\example/ + +\noindent barring readibility, obviously. + +As for the detail, \paralist is a list of space-delimited parameters, +followed by a colon. Any space before the colon is removed, that's why + +\example +\setparameter Zappa Boulez: ... \par +\setparameter Zappa Boulez : ... \par +\example/ + +\noindent set the attributes of the same \verb/Zappa/ and \verb/Boulez/ parameters. +Which means, of course, that you can set the attributes of as many parameters +as you wish by doing so. + +The name of each parameter should be fully expandable. It can contain spaces, +but then it should be enclosed between braces, since space delimits parameters +in \paralist. + +Finally, you can use \com\setparameter several times on the same parameter(s), +by default it does the same thing as using one big \com\setparameter. I say +\ital{by default}, because things might occur between the two calls, e.g. the +parameter might be active and have deleted its own attributes after the first +call, which anyway is something we'll see later. + +Like \paralist, \attrlist is a list of space-delimited attributes. +Each \attr is made of the same thing as a \param: i.e. anything expandable. +However, at the beginning of a \attrlist, the strings \verb/e:/, \verb/g:/ and \verb/x:/ +have a special meaning: these prefixes are used to specify +how an attribute or list of attributes is to be defined, and they are similar to \com\edef, +\com\gdef and \com\xdef (with no prefix meaning \com\def), i.e.: + +\example +\setparameter foo : + one = \whatever + e: two = \whatever + g: three = \whatever + x: four five = \whatever +\example/ + +\noindent defines, for the parameter \verb/foo/, the attributes \verb/one/ +and \verb/three/ as \com\whatever and the attributes \verb/two/, \verb/four/ and \verb/five/ +as the full expansion of \com\whatever; moreover, \verb/one/ and \verb/two/ +are locally defined, whereas \verb/three/, \verb/four/ and \verb/five/ are globally +defined. The space between the prefix and the name of the parameter is +optional. + +Finally, \value may be given in three ways. First, it can be delimited by +a space, e.g.: + +\example +\setparameter Zappa : + firstname = Frank + motto = {Music is the best} + ... +\example/ + +\noindent here the space is simply the end of the line. The spaces in +\verb/Music is the best/ aren't seen, because of the braces (which will +be removed from the value). Don't forget that control sequences eats +the subsequent space, hence a control sequence can't be at the end of +a value supposedly delimited by space without braces. Actually, it can't be at +the beginning either (see below). + +Otherwise, \arg{value} can be given between double quotes: + +\example +\setparameter Zappa : + firstname = "Frank" + motto = "Music is the best" + hairstyle = "\mustache" +\example/ + +\noindent which basically act as braces. The space inserted here by the +end of the line is optional and the following can be done: + +\example +\setparameter Zappa:firstname="Frank"motto="Music is the best"...\par +\example/ + +\noindent Finally, there's a special rule. Control sequences gobble the +next space, but a \value can be made of one and only one control sequence, +e.g.: + +\example +\setparameter Zappa : + hairstyle = \mustache + ... +\example/ + +\noindent i.e. if \yax sees a control sequence at the beginning of a value +it will take this control sequence only as \arg{value}, which means that +anything thereafter will be considered as belonging to the next \param +name. Hence, all the following are bad ideas: + +\example +\setparameter bad values : + % the space stops the value to "bad" + one = bad space + % the control sequence is taken as the only token + two = \control sequence + % "\sequence" eats the delimitating space + three = control\sequence +\example/ + +\noindent but they'd all be ok with quotes or braces (which +still require a space after): + +\example +\setparameter good values : + one = "bad space" + two = "\control sequence" + three = {control\sequence} + four = \LonelyCommand + five = "\Command\Command" +... +\example/ + +\noindent Finally, they are some exceptions to this rule: first, +always put \com\par between braces, otherwise it will be seen +as the end of the parameter. Second, always put a character denotation +between either braces or quotes, even if it's the only control sequence. +If you don't know what character denotation means, never mind. Just remember +that \com\bgroup and \com\egroup are character denotations. + +Now I'm sure you ask: what is this lousy syntax? An answer is I don't +like braces. Another answer is \yax is not designed to store complicated +strings, although it can do it. Instead it aims at setting simple values +in an orderly fasion, e.g.: + +\example +\setparameter page : + pagewidth = 30cm + pageheight = 32cm + top bottom = 2cm + lines = 45 + whatever = \foo +... +\example/ + +\noindent in which case it is very handy, especially when you're in a hurry: + +\example +\setparameter page: pagewidth=32cm pageheight=30cm whatever=\foo...\par +\example/ + +\noindent Thus, I find commas to delimit values equally superfluous and find +the odd quote better. + +However, good old key-value pairs separated by commas happen to be useful +too, for instance when a command takes some options. Hence the following +was introduced in v.\yaxversion: + +\describe*\setparameterlist\paralist\oarg{optional macro}\arg{attribute(s) = value list}. +This produces exactly the same thing as \com\setparameter, only the syntax +changes. Ignoring the optional argument in the middle, our example just above +would be rewritten as: + +\example +\setparameterlist{page}{pagewidth = 32cm, pageheight = 30cm, whatever=\foo} +\example/ + +\noindent Here you can forget about how values are given: the delimiter is +the comma, end of story. There can be several parameters in the first argument, +and several attributes before each `\verb"="' sign in the second; in both cases, +they are separated by spaces, as above. Also, each list of attributes can be +prefixed with \verb"e:", \verb"g:" and \verb"x:", again as with \com\setparameter. +Finally, as shown in the example, space is trimmed away. + +But \com\setparameterlist has a feature that \com\setparameter doesn't have. +If one of the \attr = \value pair doesn't contain a `\verb"="', i.e. if it +isn't a pair at all, then one of the following happens: if there is no +\arg{optional macro}, then the default value `\verb"true"' is assigned to +all the attributes mentionned; besides, the entry can still be prefixed with +\verb"e:", \verb"g:" or \verb"x:". For instance, the following two statements +are equivalent: + +\example +\setparameterlist{Zappa}{guitarist composer} +\setparameterlist{Zappa}{guitarist composer = true} +\example/ + +On the other hand, if the \arg{optional macro} is present, then it should +take one argument, and the entry is passed to it (trimmed, though). What +happens then is none of \yax's concern, although of course the macro can +itself set parameters. For instance, in the following, \verb"a" and \verb"b" +will be set to `\verb"what"' and `\verb"are you kidding"', whereas `\verb"eddie?"' +is passed to \com\dosomething. + +\example +\def\dosomething#1{ ... #1 ... } +\setparameter{whatever}[\dosomething]{a = what, b = are you kidding, eddie?} +\example/ + +Another way to set a parameter is as follows: + +\describe*\copyparameter\paralist:\param\arg{space}. +\describe\gcopyparameter\paralist:\param\arg{space}. +All the attributes of the parameters in \paralist are copied +to \param; if the latter already has some attributes, they're +aren't deleted (but you can use \com\deleteparameter beforehand, +see below) although they might be overwritten. If several parameters +in \paralist have the same attribute, the value of the last parameter +in the list wins. The difference between the two versions is that +the second is global. The space shouldn't be forgotten, it's the +same as explained in the \paramatt syntax \jumplink{values}{\ital{below}}. + + +Finally, here's one fast way of setting a single attribute: + +\describe*\setattribute\paramatt=\value\arg{space}. +\describe\esetattribute\paramatt=\value\arg{space}. +\describe\gsetattribute\paramatt=\value\arg{space}. +\describe\xsetattribute\paramatt=\value\arg{space}. +This sets \attr (no list) for \param (no list) to \value; see +\jumplink{values}{\ital{below}} for the \paramatt syntax. Note that the \arg{space} is for real, +so don't forget it. The `=' sign may be surrounded by optional space. +This command can be used instead of \com\setparameter for two reasons: +first, it is much faster (because it doesn't take lists into account); +second, if \param is active it is \ital{not} executed (see the section +\jumplink{defpar}{\ital{Defining parameters}}). +The \verb"e-", \verb"g-" and \verb"x-"versions sets the attribute +with the \verb"e:", \verb"g:" and \verb"x:" prefixes respectively. + +\describe*\deleteattribute\paramatt. +\describe\gdeleteattribute\paramatt. +This deletes \paramatt, which now responds negatively +to all previous commands, as if it was never defined. +The second version makes this deletion global, the first +keeps it local. + +\describe*\deleteparameter\paralist:. +\describe\gdeleteparameter\paralist:. +This deletes all paramaters in \paralist; it is equivalent to using +the previous command on all the parameters' attributes. The +first version is local, the second is global. And yes, the colon is +really there, although it might get away. + + + +\sectioncommand{section}{The meta attribute}{The \ital{meta} attribute} +\noindent You can give any attribute to any parameter (unless they're restricted, +but that's not the point for the time being). +However, there's one particular attribute which has a special meaning: \verb/meta/. +The value of \verb/meta/ should be another parameter. Then, when querying +the value of an attribute, say \verb/attr/, for a given parameter, say \verb/param/, +\yax will do the following: if \verb/param/ has \verb/attr/, it is returned. +Otherwise, if \verb/param/ has a \verb/meta/ attribute, whose value is for instance +\verb/metaparam/, then \verb/attr/ is queried for the value of \verb/metaparam/. +And if \verb/metaparam/ has no \verb/attr/ but has a \verb/meta/ attribute, +this process continues, until either a parameter is found with \verb/attr/ +or there are no new \verb/meta/. For instance: + +\example +\setparameter A : attr = value ... \par +\setparameter B : meta = A ... \par +\setparameter C : meta = B ... \par +\example/ + +\noindent If parameter \verb/C/ has no \verb/attr/, then it retrieves it +from parameter \verb/A/ via \verb/B/ (or from \verb/B/ if it has one). On +the other hand, if \verb/C/ has \verb/attr/, then its own value is returned. +You can also query the value of an attribute for a parameter and forbid the +search for \verb/meta/'s, as explained in the next section. + +Don't be afraid to create loop with \verb/meta/. The following is perfectly +legitimate: + +\example +\setparameter A : meta = C ... \par +\setparameter B : meta = A ... \par +\setparameter C : meta = B ... \par +\example/ + +\noindent and it can even be useful. \yax detects loops when searching +\verb/meta/ paths and stops in time (with no value, obviously). Finally, +you can set \verb/meta/ to a parameter that doesn't exist. It will simply +return no value. + +The use of \verb/meta/ is useful to create families +of parameters and/or to set default values, e.g.: + +\example +\setparameter mammal : + egg = no + fur = yes + ... +\setparameter cat whale : + meta = mammal + ... +\setparameter cat : \setparameter whale : + foot = clawed fur = no + tooth = fang foot = flipper + ... tooth = baleen + ... +\setparameter tiger mykitty : + meta = cat + stripped = yes + ... +\setparameter tiger : \setparameter mykitty : + foot = "very bad news" foot = "bad news" + ... ... +\example/ + +Apart from that, \verb/meta/ behaves as any other attribute, i.e. it can +be freely set and queried. + + + + + + + + +\section{Using values}[values] + +\noindent Once attributes have been set, they can be queried by the macros +that follow. But first, one last bit of odd syntax: \paramatt means anything +up to the colon as the \param and then anything up to the next space as +the \attr. That's the reason why space in attribute names is a bad idea: +the space is the main delimitator when using attributes. It is gobbled +in the process. On the other hand, +any space surrounding the colon is removed, so that `\verb/zappa:hairstyle/ ' +and `\verb/zappa : hairstyle/ ' denote the same attribute of the same parameter. +Good news, though: if you don't like that syntax, the next section explains +how to create commands with the same meaning but a different syntax. + +In what follows, fully expandable commands are marked with \color{.8 0 0}{\char"E01F}. + +\describe*!\nometa\arg{command}. +All the commands that follow except \com\parameterloop can be prefixed with \com\nometa. In this case, +\yax will return the value of the attribute for the specified parameter, +as usual, or return no value, i.e. it will not search \verb/meta/ parameters. +So, in what follows, `\paramatt is defined' means two things. If \com\nometa +isn't used, it means that \param has \attr or it has a metaparameter with +\attr; on the other hand, if \com\nometa is used, it means that \param has +\attr, end of story. + +\describe*!\ifattribute\paramatt\true\false. +This returns \true if \paramatt is defined, \false otherwise. Since all +commands below always check whether \paramatt is defined before trying +to do anything with the value, this command can be avoided most of the +time. + +\setparameter musician : + job = music + +\setparameter Zappa : + meta = musician + firstname = Frank + + +\Example +\setparameter musician : job = music\par +\setparameter Zappa : + meta = musician + firstname = Frank + + % See this space? +\ifattribute Zappa : job {Good, it is.}{...}\par +\nometa\ifattribute Zappa : job {...}{Too bad.} +\Example/ + + + + + +\describe*!\usevalue\paramatt. +\describe!\usevalueor\paramatt\orvalue. +\describe!\usevalueand\paramatt\andvalue\orvalue. +The first macro returns the value of \paramatt if it exists, or +does nothing otherwise. Like all the \verb/-or/ and \verb/-and/ +variants below, \com\usevalueor executes \orvalue in case +\paramatt doesn't exist, while \com\usevalueand returns +the value of \paramatt immediately followed by \andvalue +(no brace added) if \paramatt exists, otherwise it executes +\orvalue. + +\Example +Zappa's job was \usevalueand Zappa : job + { (and then some!)} + {unknown} +and he played the \usevalueor Zappa : instrument + {guitar}. +\Example/ + + +\iflines4{}{\breakpage} +\describe*!\passvalue\codarg\paramatt. +\describe!\passvalueor\codarg\paramatt\orvalue. +\describe!\passvalueand\codarg\paramatt\andvalue\orvalue. +These return \codarg\barg{value} if \paramatt is defined, +with the \verb/-or/ and \verb/-and/ variants as above. + +\Example +\def\whichwas#1{(which was #1)} +Zappa's job \passvalue\whichwas Zappa : job \ took +most of his time, because it's a time-consuming +occupation \nometa\passvalueor\whichwas Zappa : job + {(you know which)}. +\Example/ + +\describe*!\passvaluenobraces\codarg\paramatt. +\describe!\passvaluenobracesor\codarg\paramatt\orvalue. +\describe!\passvaluenobracesand\codarg\paramatt\andvalue\orvalue. +These are the same as \com\passvalue and variants except the +value of the attribute is concatenated to \codarg without braces +(which means that no braces are added in the process, not that +braces are removed from the value if it has any). + +\describe*\settovalue\arg{dimen or count}\paramatt. +\describe\settovalueor\arg{dimen or count}\paramatt\orvalue. +\describe\settovalueand\arg{dimen or count}\paramatt\andvalue\orvalue. +This sets the first argument to the value of \paramatt if it exists. +If the first argument is more than one token (e.g. \verb/\count0/ +vs. \verb/\parindent/), it must be surrounded by braces; and actually +it can even be something like \verb/\advance\count0/. Of course +\arg{dimen or count} must be a dimension or a count, and the value +of \paramatt must be accordingly a dimension +or a number (\yax doesn't check either of them). + +\Example +\setparameter para : parskip = 2pt \par +Note that +\settovalueor\parskip para : parskip + {\parskip=1pt\relax} +(\the\parskip) is basically the same thing as +\parskip=\usevalueor para : foo {1pt\relax} +(\the\parskip). +\Example/ + +What the previous example shows is that since \com\usevalue +is thoroughly expandable one can say: + +\example +\mydimen=\usevalueor parameter : attribute {0pt} +\example/ + +\noindent and it will set \com\mydimen to the value +of \paramatt or to \verb/0pt/. The difference with \com\settovalueor +is that in the construction with \com\usevalueor the assignement +is obligatorily made (hence the \verb/-or/ variant), whereas +with \com\settovalueor the or-clause can do something else +(e.g. send an error message). And \com\settovalue insert a +prophylactic \com\relax. + + + + +\describe*\storevalue\comarg\paramatt. +\describe\storevalueor\comarg\paramatt\orvalue. +\describe\storevalueand\comarg\paramatt\andvalue\orvalue. +These define \comarg as the value of \paramatt if it exists. + +\Example +\setparameter Zappa : hairstyle = \moustache\par +\storevalue\beard Zappa : hairstyle +\meaning\beard +\Example/ + + + +\describe*!\ifvalue\paramatt=\value\ \true\false. +This returns \true if the value of \paramatt is \value +and \false otherwise (including unexisting \paramatt). +Note that when comparing the value of \paramatt with +\value, catcodes aren't part of the picture. +Here \value is delimited by the following space, but +there might be optional space after the `\verb/=/' sign. +Because of this, it is not possible to test for the +emptyness of a value with \com\ifvalue, i.e. + +\example +\ifvalue foo : bar = {} {true}{false} +\example/ + +\noindent won't work. Instead, either use \com\ifcasevalue below or \com\passvalue +with an emptyness-tester (e.g. \verb/texapi/'s +\com\ifemptystring, since \yax is based on \verb/texapi/ +(what, me, self-advertising?)). + +\Example +\bgroup \catcode`\Z=13 +\setparameter foo : g: bar = Z \par +\egroup +\edef\foobar{% + \ifvalue foo : bar = Z {yes}{no}, + even though catcodes are different.} +\meaning\foobar +\Example/ + + + +\iflines2{}{\breakpage} +\describe*!\ifcasevalue\paramatt. + \describe\val\value\ \codarg. + \describe\val\value\ \codarg. + \verb/.../ \par\noindent + \describe\elseval \codarg. + \describe\endval\relax. +This executes \codarg following \value matching +the value of \paramatt. If \paramatt doesn't exist, or +matches no \value, then \com\elseval is executed. Once again +catcodes aren't taken into account when values are compared. +The exact syntax is: \value is anything from \com\val to the +next space, and \codarg is anything that follows up to the next +\com\val, \com\elseval or \com\endval (any space on the right +is removed, so no need to stick \com\val to \codarg). +Apart from \com\endval, everything here is optional: there might +be as many \com\val-clauses as needed, including none, and the +\com\elseval-clause need not be present (in which case, if no +match occurs, nothing happens). Finally, although this is +similar to \TeX's primitive \com\ifcase, there's no need +to jump before anything with \com\expandafter to avoid +bumping into conditional structure. + +\Example +\def\doitalic#1{{\it#1}} +\setparameter type : font = italic \par +\ifcasevalue type : font + \val italic \doitalic + \val bold \dobold +\endval{Some text.} + +\edef\foo{% + \ifcasevalue type : font + \val bold This is bold + \elseval This is something else + \endval} +\meaning\foo +\Example/ + + + +\describe*!\parameterloop\paralist:\arg{code}. +This loops on all the attributes for all the parameters in \paralist, +and passes the following thing to \arg{code}: the parameter, the attribute, +and the value. Hence \arg{code} can be anything, but it should be prepared +to handle three arguments. The order in which parameters are enumerated is the +order in \paralist, and for attributes it is the order in which they were set +for a given parameter. Only actual attributes are enumerated: if you loop over +\verb"ParameterA", which doesn't have \verb"AttributeA" but has a \verb"meta" +parameter with that attribute, then \verb"AttributeA" will \emph{not} be +enumerated in the loop. + +\Example +\setparameter musician : job = music\par +\setparameter zappa : + meta = musician + instrument = guitar + +\def\showvalues#1#2#3{#1 : #2 = #3\par} +\parameterloop musician zappa : \showvalues +\Example/ + + +\section{Using another syntax} + +\noindent If you don't like \yax's native syntax, and want for instance +good old braces to delimit \param and \attr, you might be tempted +to do something like: + +\example +\def\myusevalue#1#2{\usevalue #1:#2 } +\example/ + +\noindent On the other hand, if you don't mind \yax's syntax +but want other names for the commands, then you'll probably go: + +\example +\let\myusevalue\usevalue +\example/ + +\noindent Both are bad ideas. Indeed, in neither example will +\com\myusevalue work properly with \com\nometa. Besides, you +have to create the \verb/-or/ and \verb/-and/ variants by hand. +Not to mention that in the first example \com\myusevalue wastes +time calling \com\usevalue (and what if it is redefined?) when +it could be in direct relation with internal code. So here's how +to circumvent \yax's syntax and/or create new names. + +\describe*\newsyntax\arg{syntax}\barg{prefix}. +This creates commands whose names are \verb/\/\arg{prefix}\comarg +and whose syntax for arguments is \arg{syntax}. The latter is +a parameter text which must contain \verb/#1/ and \verb/#2/ (for +\param and \attr respectively) with whatever to delimit them. +For instance: + +\example +\newsyntax#1#2{x} +\newsyntax#1 #2!{y} +\example/ + +\noindent will create among others an \com\xusevalue command whose usage is +\com\xusevalue\param\attr and a \com\yusevalue command whose +usage is \com\yusevalue\param\ \attr\tcode! and both will do the +same thing as \com\usevalue. There must be braces around \arg{prefix} +and none around \arg{syntax} (i.e. the latter is delimited by +the left brace of the former). If \arg{prefix} is empty, +you redefine the default commands, which is dangerous. + +To be precise, the commands copied are (omitting the \verb/-or/ +and \verb/-and/ variants, which are created too, if any): +\com\setattribute, \com\esetattribute, \com\gsetattribute, \com\xsetattribute, +\com\ifattribute, \com\usevalue, \com\passvalue, \com\passvaluenobraces, +\com\settovalue, \com\storevalue, \com\ifvalue, \com\ifcasevalue, +\com\deleteattribute and \com\restrictattribute (which you'll learn +about in the next section). + +\describe*\copysyntax\arg{prefix1}\arg{prefix2}. +This defines all the commands above with \arg{prefix1} as +those same commands with \arg{prefix2}. + +\describe*\letyaxcommand\arg{command1}\arg{command2}. +This at the very least \com\let \arg{command1} to \arg{command2}. +Besides, if \arg{command2} can take a \com\nometa prefix, +\arg{command1} can too. Finally, if \arg{command2} has +\verb/-or/ and \verb/-and/ variants, these are created with +\arg{command1}. E.g.: + +\example +\letyaxcommand\defval\storevalue +\example/ + +\noindent defines \com\defval, \com\defvalor and \com\defvaland +as \com\storevalue and its variants. If \arg{command2} has been +created with \com\newsyntax or \com\copysyntax, \arg{command1} +of course has the same syntax. + + +\Example +\newsyntax#1#2{x} +\letyaxcommand\uv\xusevalue +\uvand{noparameter}{noattribute}{yes}{no} +\Example/ + + +\section{Restrictions on parameters and attributes} + + +\describe*\restrictparameter\paralist:\attrlist\tcode{\char`\\par}. +After this declaration, the \param's in \paralist (where they are separated +by space as in \com\setparameter) can take only those \attr's in +\attrlist (which are also separated by space). It affects \com\setparameter +only, producing an error message when an \attr not belonging to +\attrlist is given a value (and the assignment isn't made, of course). +Even if it doesn't belong to \attrlist, the \verb/meta/ attribute is +always allowed. Several \com\restrictparameter declarations on the +same parameter(s) actually accumulate the allowed attributes in \paralist, +e.g. after + +\example +\restrictparameter foo : one two three\par +\restrictparameter foo : four\par +\example/ + +\noindent one can set the attributes \verb/one/, \verb/two/, \verb/three/ +and \verb/four/ for \verb/foo/, and not only \verb/four/. The idea behind +\com\restrictparameter is not so much hiding attributes from the user +as making the use of a parameter clearer by indicating which attributes +are in use with it, especially if it can be executed (see below). + +\describe*\restrictattribute\paramatt\ \vallist\tcode{\char`\\par}. +This restricts \paramatt to take only the values in \vallist (separated +by space). + +\describe*\restrictallattributes\attr\ \vallist\tcode{\char`\\par}. +This restricts \attr, whatever the \param in which it appears, to +take only the values in \vallist. In this command, \attr is +found as anything before the first space, e.g.: + +\example +\restrictallattributes attribute value1 value2 value3\par +\restrictallattributes {attri bute} value1 value2 value3\par +\example/ + +\noindent (the second example if you want to have space in attribute +names). Note that if an attribute is restricted with both +\com\restrictattribute and \com\restrictallattributes, +only the former restriction holds. E.g.: + +\example +\restrictattribute foo:bar one\par +\restrictallattributes bar two three\par +\setparameter foo: + bar = two % Will produce an error message. + ... +\example/ + + + +\section{Defining parameters}[defpar] + +\describe*\defparameter\paralist\barg{definition}. +Parameters aren't just a way of organizing attributes. They +can have a definition and act as commands whose arguments +are the values of their attributes. The \paralist (with +parameters once again separated by space) must be +braceless, whereas \arg{definition} must be enclosed in +braces (like a real definition). No parameter text is +allowed. However, \arg{definition} can contain `\verb/#1/', +which doesn't refer to any argument but to the parameter +being defined instead, so that one can use the commands +defined in the previous section without specifying the name +of the parameter. E.g. + +\example +\defparameter foo bar {% + \usevalue #1 : one + \passvalueor\mycomm #1 : whatever {...}% + ...} +\example/ + +\noindent defines \verb/foo/ and \verb/bar/ respectively +to + +\example +\usevalue foo : one +\passvalueor\mycomm foo : whatever {...}% +... +\usevalue bar : one +\passvalueor\mycomm bar : whatever {...}% +... +\example/ + +\describe*!\executeparameter\param:. +This executes the definition of \param with the the latest values +of its attributes. If \param hasn't been defined, nothing happens. + +\Example +\defparameter foo {The value is \usevalue#1:bar } +\setparameter foo : bar = whatever\par +\executeparameter foo: +\Example/ + + +\describe*\defactiveparameter\paralist\barg{definition}. +This does the same thing as \com\defparameter, i.e. define the +parameters in \paralist, but it also set them as `active,' +which means that they're automatically executed each time +their attributes are defined with \com\setparameter. You +can still use \com\executeparameter. + +\Example +\setparameter metasection : skip = 2 font = \it\par +\setparameter mysection : % not yet active + meta = metasection\par +\defactiveparameter mysection {% + \vskip \usevalueor #1 : skip 0\baselineskip + {\usevalue #1 : font \usevalue #1 : title }% + \par} + +And now we're going to have a new section. +\setparameter mysection : title = "A new section"\par +Fascinating. Once again? +\setparameter mysection : title = "Once again"\par +Cool. +\Example/ + + +%{\it\noindent See next page for the last example.} + + +\Example +\setparameter metasection : + skip = 2 + inline = false + font = \it + +\setparameter mysection mysubsection : + meta = metasection + +\setparameter mysection : + font = \sc + +\setparameter mysubsection : + skip = 1 + inline = true + +\defparameter mysection mysubsection {% + \vskip \usevalueor #1 : skip 0\baselineskip + {\usevalue #1 : font \usevalue #1 : title }% + \ifvalue #1 : inline = true {. \ignorespaces}% + \par}% + +\def\section#1{% + \setparameter mysection : title = {#1}\par + \executeparameter mysection :} + +\def\subsection#1{% + \setparameter mysubsection : title = {#1}\par + \executeparameter mysubsection :} + + +... and this is the end of our paragraph. +\section{New ideas} +Here we are going to expose bold new ideas. +\subsection{First bold new idea} +Lore, aim, hip, sum... what do you think about +it? Pig latin, you say? +\subsection{Second bold new idea} +Perhaps Do lore, aim... then, huh? +\section{New new ideas} +Etc. +\Example/ + +\finishpdffile +\bye diff --git a/macros/generic/yax/yax.sty b/macros/generic/yax/yax.sty new file mode 100644 index 0000000000..7742f1d6f1 --- /dev/null +++ b/macros/generic/yax/yax.sty @@ -0,0 +1,14 @@ +% This is yax.sty, a style file to load +% the YaX package in LaTeX. Useful information +% can be found in yax-doc.pdf +% +% Author: Paul Isambert. +% E-mail: zappathustra AT free DOT fr +% Comments and suggestions are welcome. +% Date: January 2011. + +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{yax}[2010/01/22 v.1.03 Yet Another Key System.] +\chardef\formatnumber=4 +\input yax.tex +\endinput diff --git a/macros/generic/yax/yax.tex b/macros/generic/yax/yax.tex new file mode 100644 index 0000000000..3bf7429841 --- /dev/null +++ b/macros/generic/yax/yax.tex @@ -0,0 +1,707 @@ +% 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: January 2011. +% +\ifcsname yax@already_loaded\endcsname + \expandafter\endinput +\fi + +\input texapi % Everything I need. It figures: I wrote it for this. + +\ifdefined\texapiversion + \ifdim\texapiversion pt<1.03pt + \senderror{YaX}{You need texapi with version at least 1.03. I quit} + \expandafter\expandafter\expandafter\endinput + \fi +\else + % \texapiversion was introduced in v.1.02. + \senderror{YaX}{You need texapi with version at least 1.03. I quit} + \expandafter\endinput +\fi + +\setcatcodes{@_=11} +\def\yaxversion{1.03} +\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{<parameter>}{<attribute>} +\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 <list of parameters> : +% <prefix><attribute1> = <value1> +% <prefix><attribute2> = <value2> +% ... +% <prefix><attributeN> = <valueN> +% \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@setparameter_getpairs{}{#2 }% + \expandafter\yax@space_loop\expandafter{\expandafter\yax@setparameter_assign\expandafter{\yax@setparameters_pairs}}{#1 }% + \yax@space_loop\yax@setparameter_execute{#1 }% + } +\newfor\yax@setparameter_getpairs{1}#2={% + \pausefor{\skipspace{\passtrim{#2}{\yax@setparameter_checkprefix{\yax@setparameter_howgiven{#1}}}}}% + }[\def\yax@setparameters_pairs{#1}] + +\newstring{e:} \newstring{g:} \newstring{x:} +\long\def\yax@setparameter_checkprefix#1#2{% + \ifprefix{e:}{#2} + {\removeprefixand{e:}{#2}\passtrim{#1\edefcs}} + {\ifprefix{g:}{#2} + {\removeprefixand{g:}{#2}\passtrim{#1\gdefcs}} + {\ifprefix{x:}{#2} + {\removeprefixand{x:}{#2}\passtrim{#1\xdefcs}} + {#1\defcs{#2}}}}% + } + +\long\def\yax@setparameter_howgiven#1#2#3{% + \ifnextnospace" + {\yax@setparameter_getquote{#1}{#2}{#3}} + % No need for a new nospace: it has been removed + {\ifcatnext\foo + {\yax@setparameter_getcommand{#1}{#2}{#3}} + {\yax@setparameter_getspace{#1}{#2}{#3}}}% + } + +\long\def\yax@setparameter_getquote#1#2#3"#4"{% + \skipspace{\resumefor\yax@setparameter_getpairs{#1{#2}{#3}{#4}}}% Better not to forget this! + } +\long\def\yax@setparameter_getcommand#1#2#3#4{% + \skipspace{\resumefor\yax@setparameter_getpairs{#1{#2}{#3}{#4}}}% + } +\long\def\yax@setparameter_getspace#1#2#3#4 {% + \skipspace{\resumefor\yax@setparameter_getpairs{#1{#2}{#3}{#4}}}% + } + +\long\def\yax@setparameter_assign#1#2{% + \yax@setparameter_assignloop{#2}{#1}% + } + +\newfor\yax@setparameter_assignloop{1}#2#3#4{% + \yax@space_loop{\yax@setparameter_do{#1}{#2}{#4}}{#3 }% + } + +\newif\ifyax@makedef +% #1 = parameter; #2 = \<|e|g|x>defcs; #3 = value; #4 = attribute +\long\def\yax@setparameter_do#1#2#3#4{% + \yax@makedeftrue + \iffcs{yax@#1@restricted}% Parameter restricted. + {\reverse\iffcs{yax@#1:#4@allowed}% Attribute not allowed. + {\yax@makedeffalse + \yax@error{Attribute `#4' is not allowed in parameter `#1'. + Authorized attributes are `\unexpandedcs{yax@#1@restricted}'}}}% + \ifcs{yax@#1:#4@restricted}% Attribute restricted in this parameter. + {\reverse\iffcs{yax@#1:#4:\detokenize{#3}@allowed}% Value not allowed. + {\yax@makedeffalse + \yax@error{Value `\detokenize{#4}' is not allowed in attribute `#1:#4'. + Authorized values are `\unexpandedcs{yax@#1:#4@restricted}'}}}% + {\iffcs{yax@attribute@#4@restricted}% Attribute restricted in all parameters. + {\reverse\iffcs{yax@attribute@#4:\detokenize{#3}@allowed}% Value not allowed. + {\yax@makedeffalse + \yax@error{Value `\detokenize{#3}' is not allowed in attribute `#4'. + Authorized values are `\unexpandedcs{yax@attribute@#4@restricted}'}}}}% + \ifyax@makedef + \reverse\iffcs{yax@#1@attributelist}{\gdefcs{yax@#1@attributelist}{}}% + \ifx#2\gdefcs + \passexpandedcs{\yax@parameterlist_add\global{#1}{#4}}{yax@#1@attributelist}% + \else\ifx#2\xdefcs + \passexpandedcs{\yax@parameterlist_add\global{#1}{#4}}{yax@#1@attributelist}% + \else + \passexpandedcs{\yax@parameterlist_add{}{#1}{#4}}{yax@#1@attributelist}% + \fi\fi + #2{yax@#1:#4}{#3}% + \fi + } + +\newfor\yax@parameterlist_add{3}#4{% + \iffstring{#3}{#4}{\breakfor{}}% + }[#1\eaddrightcs{yax@#2@attributelist}{{#3}}] + +\def\yax@setparameter_execute#1{% + \iffcs{yax@#1@active}{\usecs{yax@#1@def}}% + } +% +% \setparameterlist <list of parameters>[<default value>|*<cs>]<attribute>=<value>, ... +% +\def\setparameterlist#1{% + \ifnextnospace[ + {\yax@setparameterlist_getdefault{#1}} + {\yax@setparameterlist_getdefault{#1}[]}% + } + +\def\yax@setparameterlist_getdefault#1[#2]#3{% + \yax@setparameterlist_loop{#1}{#2}{#3,}% + \yax@space_loop\yax@setparameter_execute{#1 }% + } + +\newstring{=} +\newfornoempty\yax@setparameterlist_loop{2}#3,{% + \ifcontains={#3} + {\yax@setparameterlist_getargs{#1}#3\yax@endofentry} + {\ifemptystring{#2} + {\yax@setparameterlist_getargs{#1}#3=true\yax@endofentry} + {\passtrim{#3}{#2}}}% + } + +\long\def\yax@setparameterlist_getargs#1#2=#3\yax@endofentry{% + \yax@setparameter_checkprefix{\passtrim{#3}{\yax@setparameterlist_assign{#1}}}{#2}% + } + +\long\def\yax@setparameterlist_assign#1#2#3#4{% + \yax@space_loop{\yax@setparameterlist_do{#3}{#2}{#4}}{#1 }% + } + +\long\def\yax@setparameterlist_do#1#2#3#4{% + \yax@space_loop{\yax@setparameter_do{#4}{#1}{#2}}{#3 }% + } +% +% \setattribute <parameter> : <attribute> = <value> +% +\def\yax@removesign#1#2#3#4={#1{#2}{#3}}% +\pdef\setattribute{% + \yax@getall{\yax@removesign{\yax@setattribute\defcs}}% + } +\pdef\esetattribute{% + \yax@getall{\yax@removesign{\yax@setattribute\edefcs}}% + } +\pdef\gsetattribute{% + \yax@getall{\yax@removesign{\yax@setattribute\gdefcs}}% + } +\pdef\xsetattribute{% + \yax@getall{\yax@removesign{\yax@setattribute\xdefcs}}% + } +\def\yax@setattribute#1#2#3{% + \skipspace{\yax@setattribute_getvalue{#1}{#2}{#3}}% + } +\long\def\yax@setattribute_getvalue#1#2#3#4 {% + \yax@setparameter_do{#2}{#1}{#4}{#3}% + } +% +% \copyparameter <list of parameters> : <parameter> +% +\pdef\copyparameter{% + \yax@getall{\yax@copyparameter{}}% + } +\pdef\gcopyparameter{% + \yax@getall{\yax@copyparameter\global}% + } +\def\yax@copyparameter#1#2#3{% + \reverse\iffcs{yax@#3@attributelist}{#1\letcs{yax@#3@attributelist}\emptycs}% + \yax@space_loop{\yax@copyparameter_do{#1}{#3}}{#2 }% + } +\def\yax@copyparameter_do#1#2#3{% + \iffcs{yax@#3@attributelist}{% + \passexpandedcs{\yax@parameterlist_copy{#1}{#3}{#2}}{yax@#3@attributelist}}% + } +\newfor\yax@parameterlist_copy{3}#4{% + #1\letcstocs{yax@#3:#4}{yax@#2:#4}% + \passexpandedcs{\yax@parameterlist_add{#1}{#3}{#4}}{yax@#3@attributelist}% + } +% +% +% +% \deleteparameter <list of parameters>: +% +\pdef\deleteparameter{% + \yax@getparameter{\yax@deleteparameter{}}% + } +\pdef\gdeleteparameter{% + \yax@getparameter{\yax@deleteparameter\global}% + } +\def\yax@deleteparameter#1#2{% + \yax@space_loop{\yax@deleteparameter_do{#1}}{#2 }% + } +\def\yax@deleteparameter_do#1#2{% + \iffcs{yax@#2@attributelist}{\passexpandedcs{\yax@parameterlist_delete{#1}{#2}}{yax@#2@attributelist}}% + } +\newfor\yax@parameterlist_delete{2}#3{% + #1\letcs{yax@#2:#3}\yax@somethinghopefullyundefined + }[#1\letcs{yax@#2@attributelist}\yax@somethinghopefullyundefined] +% +% +% \deleteattribute <parameter> : <attribute> +% +\def\deleteattribute{% + \yax@getall{\yax@deleteattribute{}}% + } +\def\gdeleteattribute{% + \yax@getall{\yax@deleteattribute\global}% + } +\def\yax@deleteattribute#1#2#3{% + \iffcs{yax@#2@attributelist}{% + #1\letcs{yax@#2:#3}\yax@undefined + \passexpandedcs{\yax@parameterlist_remove{#1\edefcs}{#2}{#3}{}}{yax@#2@attributelist}}% + } + +\newfor\yax@parameterlist_remove{4}#5{% + \reverse\iffstring{#3}{#5}{\passarguments{#1}{#2}{#3}{#4{#5}}}% + }[#1{yax@#2@attributelist}{#4}] +% +% +% +% +% \restrictparameter <list of parameters> : +% <attribute1> +% <attribute2> +% ... +% <attributeN> +% \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 <parameter>:<attribute> +% <value1> +% <value2> +% ... +% <valueN> +%\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 <attribute> +% <value1> +% <value2> +% ... +% <valueN> +%\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<list of parameters>{<definition>} +% \defactiveparameter<list of parameters>{<definition>} +% \executeparameter <parameter> : +% +\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 + \yax@activeparameterfalse + } + +\def\defactiveparameter{% + \yax@activeparametertrue\defparameter + }% +\long\def\yax@defparameter_def#1{% + \edefcs{yax@#1@def}{\primunexpanded\expandafter{\yax@temp{#1}}}% + \ifyax@activeparameter + \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}% + } +% +% +% +% +%%%%%%%%%%%%%%%%%% USING VALUES %%%%%%%%%%%%%%%%%% +% +% These are the loops used to check whether <parameter>:<attribute> 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@<parameter>:<attribute> if it exists. +% => doesn't change from one iteration to the next. +% #3 = <parameter> +% => becomes \usecs{yax@<parameter>:<meta>} on each iteration, thus +% returning a new <parameter> to be searched. +% #4 = <attribute> +% => doesn't change from one iteration to the next. +% #5 = <or> if alone, <and> when #6 exists. +% #6 = <or>. +% => neither change. +% +% The loop does the following: +% If <parameter>:<attribute> exists, then execute #2 on it followed by <and> if any. +% Or, if <parameter> has a meta attribute, it calls +% the \yax@ifattribute_checkmeta subloop with <or> and <parameter> +% as passed arguments and <list of previous parameters> as the list argument. +% This command compares each item in the list with <parameter>, and if they +% have the same meta, it means we're in a meta-loop, we break and execute <or>. +% If no match obtains, we rerun the original loop (with #3 turned to its meta). +% Or, if <parameter> had no meta in the first place, we break and execute <or>. +% +\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}% <parameter> has no <attribute>. + {\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 <true> and release <false> + } +\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 <or> 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 \<com>, \<com>or and \<com>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}{\splitstringat{\elseval}{#1}\yax@ifcasevalue_doelse}% + } +\def\yax@ifcasevalue_doelse#1#2{\trim{#2}} +\def\yax@ifcasevalue_scan#1#2\endval{% + \ifcontains{\elseval}{#2} + {\splitstringat{\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] +% +% +% +% +% \parameterloop<list of parameters>:<command> +% +\def\parameterloop{% + \yax@getparameter\yax@parameterloop + } +\long\def\yax@parameterloop#1#2{% + \yax@space_loop{\yax@parameterloop_launch{#2}}{#1 }% + } +\long\def\yax@parameterloop_launch#1#2{% + \iffcs{yax@#2@attributelist} + {\passexpandedcs{\yax@parameterloop_do{#1}{#2}}{yax@#2@attributelist}}% + } +\newfor\yax@parameterloop_do{2}#3{% + \passvalue{#1{#2}{#3}}{#2}:{#3} + } +% +% +% +% +% \newsyntax +% +\long\def\newsyntax#1#{% + \yax@newsyntax{#1}% + } +\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\yax@newsyntax#1#2{% + \defcs{#2restrictattribute}#1{\yax@restrictattribute_getrestrictions{##1}{##2}}% + \defcs{#2deleteattribute}#1{\letcs{yax@##1:##2}\yax@undefined}% + \defcs{#2setattribute}#1{\yax@removesign{\skipspace{\yax@setattribute\defcs}}{##1}{##2}}% + \defcs{#2esetattribute}#1{\yax@removesign{\skipspace{\yax@setattribute\edefcs}}{##1}{##2}}% + \defcs{#2gsetattribute}#1{\yax@removesign{\skipspace{\yax@setattribute\gdefcs}}{##1}{##2}}% + \defcs{#2xsetattribute}#1{\yax@removesign{\skipspace{\yax@setattribute\xdefcs}}{##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 |