diff options
-rwxr-xr-x | Master/texmf-dist/doc/generic/gates/README | 26 | ||||
-rwxr-xr-x | Master/texmf-dist/doc/generic/gates/gates-doc.pdf | bin | 0 -> 155218 bytes | |||
-rwxr-xr-x | Master/texmf-dist/doc/generic/gates/gates-doc.tex | 125 | ||||
-rwxr-xr-x | Master/texmf-dist/doc/generic/gates/gates-doc.txt | 1820 | ||||
-rwxr-xr-x | Master/texmf-dist/doc/generic/gates/gates.tex | 925 | ||||
-rwxr-xr-x | Master/texmf-dist/tex/generic/gates/gates.lua | 695 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/gates/gates.sty | 14 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/gates/t-gates.tex | 24 | ||||
-rwxr-xr-x | Master/tlpkg/bin/tlpkg-ctan-check | 2 | ||||
-rwxr-xr-x | Master/tlpkg/libexec/ctan2tds | 2 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/collection-genericextra.tlpsrc | 1 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/gates.tlpsrc | 0 |
12 files changed, 3633 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/generic/gates/README b/Master/texmf-dist/doc/generic/gates/README new file mode 100755 index 00000000000..5af453033ae --- /dev/null +++ b/Master/texmf-dist/doc/generic/gates/README @@ -0,0 +1,26 @@ +This is the README file for the Gates package. +Author: Paul Isambert. +E-mail: zappathustra AT free DOT fr +Comments and suggestions are welcome. +Date: December 2011. +Version: 0.1. + +Gates helps writing code in a modular fashion: macros are broken into +small chunks with names, which can be externally controlled and/or +augmented with new ones. Code thus written can be easily modified. + +Gates is implemented independantly for both TeX and Lua; the former +requires the texapi package, while the latter can run with any Lua +interpreter (not just LuaTeX). + +The files in this distribution are: + +gates.tex - TeX implementation +gates.sty - wrapper for LaTeX +t-gates.tex - wrapper for LaTeX +gates.lua - Lua implementation +gates-doc.tex - master file for the doc +gates-doc.txt - text of the doc (readable in a text editor) +gates-doc.pdf - typeset doc + +Licensing of this package is covered by LPPL. diff --git a/Master/texmf-dist/doc/generic/gates/gates-doc.pdf b/Master/texmf-dist/doc/generic/gates/gates-doc.pdf Binary files differnew file mode 100755 index 00000000000..30a7ac42788 --- /dev/null +++ b/Master/texmf-dist/doc/generic/gates/gates-doc.pdf diff --git a/Master/texmf-dist/doc/generic/gates/gates-doc.tex b/Master/texmf-dist/doc/generic/gates/gates-doc.tex new file mode 100755 index 00000000000..906f9d555d5 --- /dev/null +++ b/Master/texmf-dist/doc/generic/gates/gates-doc.tex @@ -0,0 +1,125 @@ +% This is the master file producing gates-doc.pdf. The version of the +% documentation readable in a text editor is interpreter-doc.txt (input below). +% +% Paul Isambert - zappathustra AT free DOT fr - December 2011 + +\input pitex +\input interpreter + +\overfullrule=0pt + +\setfont\mainfont: + name = "Chaparral Pro" + size = 18pt + bold = Semi + big = 144pt + +\setfont\codefont: + name = "Lucida Console" + slant = 15 + bold italic = none + size = 14pt + features = "-tlig, -trep, space = mono" + +\setparameter section subsection : + color = ".8 0 0" + inline = true + toc = true + +\setparameter section : + font = "\mainfont\normalsize\sc" + number = none + clear = false + +\setparameter sectionbookmark : + open = true + italic = true + +\setparameter subsection : + font = "" + +\setparameter paragraph : + number = none + beforeskip = 1 + color = ".8 0 0" + font = "" + aftertitle = "\hskip.6em" + +\setparameter navigator : + pre = {\linkcolor"} + post = {"} + mode = bookmarks + +\def\linkcolor"#1"{% + \color{.4 .3 .3}{\ital{#1}}% + } + +\setparameter page : + width = 80pc + left right = 20pc + lines = 24 + baselineskip = 24pt + +\Section def {section_do}[2]{% + \noindent + \ifstring{#1}{section} + {\marginnote[font = \rm, + hpos = rf, + side = left, + hsize = 5cm]{\quitvmode#2}} + {\ifstring{#1}{subsection} + {\marginnote[hsize = 4cm]{\quitvmode#2}} + {#2}}% + } + +\OutputRoutine close {headers}{shipout} + +%%% Redefining \TeX to look better with Chaparral. +\def\TeX{% + T\kern-.13em + \lower.5ex \hbox {E}% + \kern-.03em X% + } +% \TeX in bookmarks. Takes an argument because Interpreter changes all "TeX" +% to "\TeX{}". +\pdfdef\TeX#1{TeX} + +\def\gatecolor{\color{.8 0 0}} +\def\gateaction#1{% + \directlua{pitex.callback.skip("french_punctuation")}% + \hbox{\gatecolor{{\codefont#1}}}% + } + +\def\describe#1#2{% + \needspace{2\baselineskip}% + \bgroup\codefont + \hsize=\maxdimen + \noindent + \gatecolor{\llap{\gateaction{\string#1}\kern.3em}% + #2\par\egroup}\removenextindent + \ignorespaces} + +\def\comarg{\arg{command}\antigobblespace} +\def\textarg{\arg{text}} + +\setbox0=\hbox{% + {\big \color{.8 0 0}{Gates}}% + \vbox{% + \hbox{Paul Isambert} + \hbox{\codefont zappathustra@free.fr} + \hbox{Version 0.1} + \hbox{December 2011}}% + } +\ht0=6\baselineskip \dp0=0pt +\box0 +\vskip2\baselineskip + +\Section skip {section_break}{section} + +\input gates + +\hyphenation{my-other-list} + +\interpretfile{pitex}{gates-doc.txt} + +\bye diff --git a/Master/texmf-dist/doc/generic/gates/gates-doc.txt b/Master/texmf-dist/doc/generic/gates/gates-doc.txt new file mode 100755 index 00000000000..9d22ebf6b46 --- /dev/null +++ b/Master/texmf-dist/doc/generic/gates/gates-doc.txt @@ -0,0 +1,1820 @@ +Gates: a presentation +===================== + +This part of the documentation is a general introduction to gates, +illustrating many of their properties in the abstract. Then comes + {a user manual for TeX} [see TeXUser_tag], + {a complete reference for TeX} [see TeXReference_tag], + {a user manual for Lua} [see LuaUser_tag] and + {a complete reference for Lua} [see LuaReference_tag]. +The manuals explain gates in action, while the references are +alphabetical lists of all actions with their syntax. + +This documentation doesn't contain very complex examples of gates. To +see gates in full regalia, you can have a look at PiTeX (the set of +files used to typeset this documentation) for (mostly) TeX gates, and +the Interpreter package, version 1.1, for Lua gates (version 1.0, wasn't +written with Gates). Speaking of Interpreter, this documentation was +written with it, so it can be read confortably in a text editor (see +"gates-doc.txt"). + + + What are gates, and what are they good for? + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +At first sight, gates are just a convoluted way to define and execute +macros. But they're better thought of as bricks to *construct* macros +in such a way that each brick can be independantly controlled, and new +bricks can be added to a wall already built. For each logical step in a +big macro, the user can decide whether to execute that step, bypass it, +add a new step before or after, etc. This way, you give the user the +ability to tweak your macro as s/he wishes. + +Suppose for instance you write \BigMacro. \BigMacro does a lot of +interesting things: first it does A, then B, then -- last but not least +-- C. The problem with \BigMacro is that it's very good; so good that +everybody uses it and wants to add his or her little twist to it, because +\BigMacro really entices creativity. You have no time to add those +twists yourself, nor do you think they'll interest anybody but their +authors. Yet you keep receiving emails asking you to modify \BigMacro. +Yes, \BigMacro is big, so most users don't care to try and understand +it; even if they do, they're not so sure how it works: in A, there seems +to happen something, and X becomes Y, and then in B, Y is passed to... +or was it X? Plus those same people really don't want to copy one hundred +lines of code just to add their touch to it. \BigMacro is like a plate +of spaghetti: good, but spaghetti nonetheless, hard to decipher and +rearrange. A few people will be able to hack it, but it is lost on users +bold but lacking experience. + +Gates are made to avoid that situation. \BigMacro will now be made of +the same logical steps, but they will be gates, which means users will +be able to control their behavior without ever looking at how they're +implemented. For instance, one person would prefer to avoid step B in +some cases; if B is a gate, that can be done easily. Another don't like +B at all and wants to remove it altogether: nothing simpler. A third +person would like to get the output of B and modify it a bit before C +kicks in; s/he just has to write a new gate (which is like writing a +macro) and add it after B; whatever B returns will be passed to that +new gate instead of C, and whatever the new gate returns will be passed +to C. That's another thing with gates: they pass arguments between them +when one follows the other; they can even return arguments. That's +nothing new in Lua; but in TeX, arguments handling is generally not so +simple. In most, if not all, programming languages, the following: + + dosomething(makesomething(<argument>)) + +is evaluated as follows: <argument> is passed to "makesomething", whose +return value is passed to "dosomething". The equivalent in TeX doesn't +do that, however: + + \dosomething{\makesomething{<argument>}} + +Here "\makesomething{<argument}" is passed to \dosomething and chaos +is sure to ensue if the previous behavior is expected. Instead, one +must generally store whatever \makesomething `returns' in a macro and +pass that macro to \dosomething. With gates, however, the passing of +arguments can be mimicked: if two gates follow each other in a list, +whatever the first returns (no quotes here) is passed to the second. +In our case, if \makesomething is called before \dosomething, the former +will process its argument and pass its return value (if any) to the +latter. No special care is needed to do so. + +Another property of gates, obviously, is that they are reusable. In the +TeX world, however, it is customary to distinguish between public macros, +to be manipulated by users, and private ones, traditionally marked by +an "@" in their names, and that lay users should absolutely ignore. +With gates, everything seems public. That's indeed the very reason why +gates exist: so that macros are as hackable as possible, and users can +do what they like with them. If you're afraid that chaos might ensue +(because users are notoriously dangerous), don't worry too much: it is +a little bit harder to grasp how gates work than to turn "@" into a +letter so it can be used in private macros; a user who would manipulate +gates thus probably knows what s/he's doing, or at least that what +s/he's doing might break. (By the way, gates are very likely to contain +"@"-marked private macros anyway, if only because they don't make sense +outside the context of their use.) + +If you want to use gates but still insist that users shouldn't mess +with them, there is another solution: don't document them. Indeed, gates +are useful only to the extent that other people know where they occur +and what they do. So documenting gates is an essential step in exploiting +them fully; as will be said again below, though, there is no need to +explain what a gate does internally: simply saying what arguments it +takes, what it does with them (conceptually), and what it returns, is +enough. In other words, you don't explain the code that makes up the +gate, you explain its function in the larger picture. + +One last remark about gates, not expanded in this document: they can +be useful to create libraries, i.e. snippets of code to be used in +various places. Instead of writing macros to perform such and such +operation, you can write gates; the difference is that they can be +easily added to existing code, and externally controlled. + + + + An overview of (almost) all actions + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are two types of gates: macro gates (or `m-gates' for short) and +list gates (`l-gates'). M-gates execute some code, like macros in TeX +or functions in Lua; l-gates contain other gates (of either type) and +call them in turn, passing arguments between them; from now on, `gate' +means a gate of either type. Typically, a big macro is an l-gate with +many subgates, themselves possibly l-gates containing gates of a lower +level still, and so on and so forth. Then one can say, `In that l-gate, +I want this gate to be ignored'; or `I want to add my own gate to this +l-gate, in such position', etc. + + + -- Defining and executing gates -- + +To define an m-gate, one uses the |def| action; the |list| action is +used to declare an l-gate. Gates of either type can then be added to +an l-gate with |add|; by default, the insertion is done at the end of +the list, but a position relative to other gates can be optionally +specified. Gates can be removed with the |remove| action. + +A gate can also be defined by |copy|ing another gate; in that case, the +new gate is identical to the copied one, except its status (see below) +is set to |open|, which is the default status when creating a gate. When +copying an l-gate, the list is copied too (also it is the same subgates +that occur in both l-gates). + +To call a gate, one uses |execute| (various shorthands are possible). +Executing an m-gate is not very different from executing a macro or +function: it performs its definition. The execution of an l-gate, on the +other hand, consists in calling the gates that were added to it. Also, +and most importantly, gates in a l-gate pass arguments between them: the +first gate receives the arguments passed to the l-gate it belongs to, +then passes them to the next gate, possibly modifying some of those +arguments by |return|ing. (Returning arguments depends heavily on the +implementation; see |return| and associates in TeX, and |autoreturn| +in Lua.) + + For each language, see: + {defining and executing gates in TeX} [see TeXDefinition_tag] + and + {defining and executing gates in Lua} [see LuaDefinition_tag]. + + + -- Conditions -- + +Gates can be externally controlled in four ways. First, all gates have +a |status|: if it is |open| (which is default), the gate is executed +when encountered; if it is |close|, it is ignored; |ajar| means that the +gate will be executed the next time it is encountered, and then its +status will revert to |close|; |skip| is the opposite: the gate will be +ignored the next time, then it will revert to |open|. In short, |ajar| +and |skip| mean executing or ignoring the gate once. + See {status in TeX} [see TeXStatus_tag] + and {status in Lua} [see LuaStatus_tag]. + +Second, all gates may have a |conditional|: if it is true, the gate is +executed, and ignored otherwise. This allows gates to depend on external +states of affairs. But a gate's conditional takes the same arguments as +the gate itself, so the its value can also depend on what is passed to +the gate. Thus, what a gate does and why it does it are kept distinct. + See {conditional in TeX} [see TeXConditional_tag] + and {conditional in Lua} [see LuaConditional_tag]. + +Third, the same gate can be repeated with a |loop|: it is the same thing +as a conditional, and the gate's arguments are also passed to it. The +gate will be called repeatedly as long as the loop is true. When a gate +is repeated, it passes its returned arguments to itself, and before +that to the loop. It is as if the same gate had been added several times +in a row to the same l-gate. + See {loop in TeX} [see TeXLoop_tag] + and {loop in Lua} [see LuaLoop_tag]. + +Finally, |loopuntil| is another kind of loop: the gate is repeated as +long as it is false; also, |loopuntil| is evaluated after the gate, so +the latter is executed at least once. If a gate has both |loop| and +|loopuntil|, the latter is ignored. + See {loop-until in TeX} [see TeXLoopUntil_tag] + and {loop-until in Lua} [see LuaLoopUntil_tag]. + +Actually, a gate doesn't have only one status, one conditional, etc. +Rather, it has one global set of conditions, and one local set of +conditions for each l-gate where it appears (note that if a gate appears +several time in the same l-gate, it has only one local set of conditions). +The global conditions are examined whenever the gate is encountered, +either when it is called directly with |execute| or when it is called +by an l-gate; local conditions are examined in the latter case only. + +It is important to understand the relative order of evaluation of the +conditions, and how global and local conditions interact. Suppose a +gate is called directly with |execute|; then the following happens: +first, the gate's global status is checked; if |ajar| or |skip|, it is +set back to |close| and |open| respectively; if the original status +allows the execution of the gate (i.e. the status was |open| or |ajar|), +the gate's global conditional is then evaluated; if it is true, and the +gate has neither |loop| nor |loopuntil|, the gate is executed once; +otherwise, it is repeated as long as |loop| is true or as long as +|loopuntil| is false. Note that even if the gate is executed several +times, the status and conditional aren't reevaluated, only the loops. + +Suppose now the gate occurs in an l-gate. Then the same evaluation +happens, this time with the local values for that given l-gate. If the +gate is deemed good for execution, or rather, each time it is executed +(if a loop calls it several time), the global values are evaluated again +as we've just seen. In other words, when a gate occurs in an l-gate, +its global conditions are examined if and only if the local conditions +allows the execution of the gate, and each time they prompt it. This +means for instance that if a gate is globally |ajar| and is encountered +in an l-gate where it's local status is |close|, its global status will +not be reverted to |open| because it will simply not be evaluated (of +course, it can be evaluated elsewhere). + +This may seem a bit complicated, but actually situations where you have +to specify both local and global conditions are rare (global status is +always a bit dangerous, because all the instances of the gate are affected, +which might not be expected if somebody reuses an existing gate); also, +you may very well |open| and |close| a gate for whatever reasons, but +if you use |ajar| and |skip| it means you're controlling it quite tightly +and you probably won't use a conditional too. + + + -- The shorthand notation -- + +Defining gates, adding them to l-gates, and setting their conditions, +can be done with actions, as explained above. But that can be tedious, +because when you're building a big macro (a big l-gate, rather), it's +hard to see the larger picture: you're adding gates one after the other, +and you can't readily figure out what the big l-gate looks like. It's +as if you were looking at a house brick by brick without being able to +take a few steps back and consider the whole building. + +But entire gates, replete with subgates down to whatever level, and +specified for all conditions, can be created without ever mentioning any +action. The overall architecture can be preserved, because gates are +defined and added to an l-gate at once. It is a bit like writing a big +macro or function, except you add a tag to each chunk of code, for further +reference. The shorthand notation isn't explained further here, because +it depends on the implementation and works very differently in TeX +and Lua: see + {TeX shorthand notation} [see TeXShorthand_tag] and + {Lua shorthand notation} [see LuaShorthand_tag]. + + + -- Gate families -- + +Since gates are designed to be hacked, it is all the more convenient if +they have simple, descriptive names. Of course, you can call a gate +"insftn", but "InsertFootnotes", or any other readable name, is definitely +better; a user can then easily browse gates and find what s/he's +looking for. + +However, such significant names increase the risk that two gates bear +the same name and clash. This is avoided by creating gate families. A +family is simply a prefix added to a name's gate, so that a gate whose +name is "MyGate" is actually called "MyFamily:MyGate". But family are +associated with calling commands in TeX and tables in Lua, so that when +a gate is mentioned without its family, it is automatically added. For +instances the \gates command in TeX and the "gates" table in Lua are +associated with the family called "gates", so the name of a gate manipulated +with them is actually "gates:<name>". + +You can define a new family, associated with a command or table, with +the |new| action; gates manipulated by that command or table will then +have a real name which includes the family. Thus, their apparent names +can be identical to other gates, as long as they belong to other families. + +As said above, a gate's family is supplied when it is missing from the +gate's name. This means conversely that if the family is specified in +a gate's name, it is not added. So, if you mention gate "MyFam:MyGate", +the family associated with the command or table where the gate is +mentioned isn't specified again. You can thus very well use gates from +other families without having to rely on the associated command or +table. In other words, there's no hidden mechanism behind families: +they're just prefixes added to a gate's name, and the automatic addition +of a family is just an examination of a gate's name. + +Although a command or table must be declared with |new| to work properly, +families themselves don't require that. Gate "MyFam:MyGate" can be used +even if "MyFam" hasn't been declared (and the declaration can also take +place later, so that the family is associated with a command or table). +In other words, the command/table-family association is just a convenient +way to make the names of your gates unique without thinking about it, +but you can also think about it and use an explicit family prefix. + See {families in TeX} [see TeXFamilies_tag] + and {families in Lua} [see LuaFamilies_tag] + + + -- If you get lost -- + +Gates can be quite complex. For instance, the big l-gate that creates +section headings in this document is made of 16 subgates and subsubgates +(as I am writing this, at least). The main function of the Interpreter +package used to turn the source of this document into proper TeX is made +of more than 25 subgates (not counting repeated ones), with 7 levels +between the top l-gate and the most deeply embedded m-gate. + +There are a few actions to make things clearer. First, you can know +whether a given gate is an m- or l-gate with |type|, which returns 1 +(for m-gates), 2 (for l-gates), or 0 (if the name you passed isn't a +gate). Similarly, a gate's status (either global or local to some l-gate) +can be queried with the |status| action, which returns 1, 2, 3 or 4, +depending on whether the gate's status is open, ajar, skip or close, +and 0 if there is no gate with the given name. + +You can loop over all the subgates in an l-gate with |subgates|, and +execute some code for each subgate. + +The family associated with a command or table can be queried by the +|family| action. + +Finally, gates can be explored more thoroughly with |show| and |trace|. +The |show| action prints (on the terminal and log file) a gate's name, +type, and global conditions (the loops are omitted if not specified); +if the gate is an l-gate, the same happens with its subgates, except +local conditions are shown; and a subgate is an l-gate itself, the +process goes on. Subgates are displayed in a manner similar to the TeX +shorthand notation. + +The |trace| action shows gates when they are encountered: it is signalled +whether they are executed or not (and why), and possibly the arguments +passed to them are mentioned too. Again, subgates to an l-gate are +marked as such. + + See {getting lost in TeX} [see TeXLost_tag] + and {getting lost in Lua} [see LuaLost_tag] + + + + + +Gates in TeX :TeXUser_tag +============ + +This part of the documentation explains how gates work in TeX. + + + + Loading and using Gates + ~~~~~~~~~~~~~~~~~~~~~~~ + +The way to load Gates depends on your format. In plain TeX, you use: + + \input gates + +In LaTeX: + + \usepackage{gates} + +And in ConTeXt: + + \usemodule[gates] + +Actions in TeX aren't executed with control sequences, but with a +calling command (by default, \gates), followed by an action's name, +followed by a space: + + \gates action {...} + +(Of course, the space can be the end of a line.) + +Note that loading Gates in TeX doesn't automatically load the Lua +counterpart even in LuaTeX; in other words, Gates in Lua should be +independantly loaded. + + + + Definition and execution :TeXDefinition_tag + ~~~~~~~~~~~~~~~~~~~~~~~~ + +Here it is shown how gates are created, concatenated, executed, and how +arguments are passed between them. + + + -- Defining gates -- + +Let's try an extremely simple example. You want to define a macro which, +when given a number, adds 3 to it, multiplies everything by 2, and prints +the result. How fascinating. Here's how you'd do it with gates. First +you define your m-gates with |def|: + + \gates def {add} [1] {% + \gates return {#1+3} + } + \gates def {multiply} [1] {% + \gates return {(#1)*2} + } + \gates def {print} [1] {% + \the\numexpr#1\relax + } + +The number of argument (up to nine) is given after the name, between +brackets, and can be omitted if the gate takes no argument. The |return| +action is discussed more thoroughly below [see Return_tag]; just note +for the moment that there is no need to add a comment at the end of the +line to avoid spurious space, because material after a |return| is +gobbled (so watch your \fi's!). + +(Instead of |def|, we could use the |edef| action; the difference is +the same as between TeX's \def and \edef. There is no \gdef/\xdef +variants, because operations on gates are always globals.) + +Now those gates should be added to an l-gate. Such a gate is declared +with |list|, and an optional number of arguments can be specified too. + + \gates list {operation} [1] + +Finally we |add| the m-gates to the l-gate: + + \gates add {add, multiply, print} {operation} + +The |add| action can take one or more gates, separated by commas. Examples +of |add| with specified position can be found below [see TeXAddWithPosition_tag]. + + + -- Executing gates -- + +And that's it, our small \BigMacro is done. We can call it, with 4 as +argument, for instance, and it will print 14; to do so, we use the +|execute| action: + + \gates execute {operation}{4} + +Note that m-gates can be executed too, although *that* is a convoluted +way to call a macro; in this case, do not worry about the return value, +it simply vanishes. For instance, the following: + + \gates execute {print}{5+2} + +will produce "7". If we'd call "multiply" rather than "print", nothing +would have happened, since it simply returns something, which doesn't +make sense outside an l-gate. + +Execution can be called more directly as: + + \gates operation {4} + +I.e. instead of a gate action, you use the name of a gate, and that's +equivalent to calling |execute| with that gate. However, this can be +done if and only if the gate you want to call doesn't have the same +name as an action; for instance, you can't do that with the "add" gate, +because there exists an action called |add|, so you have to use |execute| +instead. + + :TeXAddWithPosition_tag +Now suppose we'd like to add a substraction after the multiplication -- +that is, suppose we're a user who wants to add his or her touch to +\BigMacro. Nothing simpler: + + \gates def {substract} [1] {% + \gates return {#1-3} + } + \gates add {substract}[after multiply]{operation} + +And now "operation" returns 11 when fed 4. This was done by simply +adding an optional argument to the |add| operation; this specifies where +the new gate(s) should be added in the l-gate: by default, it is the +end of the list, but you can say "first" to put the gate(s) at the +beginning or "before <name>" or "after <name>" to make the insertion +before or after the gate called <name>; instead of a name, you can also +use "first" or "last" to denote the first and last gates of the list, +so here we could have used "before last" instead of "after multiply". + +You can also |remove| a gate from a list. After: + + \gates remove {substract}{operation} + +the l-gate "operation" is now the same as before. + + + -- Handling arguments -- :Return_tag + +One crucial properties of gates is that they pass arguments between +them. This was already illustrated by our code above: "operation" takes +one argument, which is passed to "add", "multiply" and "print"; or +rather, it is passed to "add", and that gate returns an argument which +is passed to "multiply", and again to "print". + +Arguments work as follows: a gate called with |execute| should be +followed by as many arguments as it was declared with, just like any +other macro in TeX. An l-gate passes its arguments to its first subgate; +and for two consecutive gates, the second receives what the first +returns. + +To return arguments, you use the |return| action, as illustrated above. +It expects the same number of arguments as the gate where it appears. +For instance, "add" was declared with one argument, so when it calls +|return|, one argument is expected. But one might want to return a +different number of arguments; in this case, one can use |return0|, +|return1|, |return2|, etc., up to |return9|. Also, the |return!| action +takes one big argument containing an indefinite number of arguments to +be returned; for instance: + + \gates return2 {one}{two} + \gates return! {{one}{two}} + +are equivalent, and they're also equivalent to a simple |return| with +two arguments in a gate declared as taking two arguments. + +Since l-gates execute no code, you can't call any version of |return| +with them; but they automatically return whatever was returned by their +last gate, and that is passed whatever follows the l-gate. + +Until now, we have implicitly assumed that all gates in the same l-gate +take the same number of arguments. That is not necessary: two gates +with a different number of arguments can occur in the same l-gate; +consequently, a gate doesn't need to take the same number of arguments +as the l-gate it appears in. For instance, you can very well declare +an l-gate with 3 arguments, and add to it an m-gate with 2 arguments +and another one with 4. Let's do it, and see what happens: + + \gates list {mylist} [3] + \gates def {macro1} [2] {% + \immediate\write16{1: #1. 2: #2.}} + \gates def {macro2} [4] {% + \immediate\write16{1: #1. 2: #2. 3: #3. 4: #4.}} + \gates add {macro1, macro2} {mylist} + \gates execute {mylist} {one}{two}{three} + +This will print: + + 1: one. 2: two + 1: one. 2: two. 3: three. 4: . + +The first two arguments of "mylist" are passed to "macro1", and the third +is simply ignored. Then the three arguments are passed to "macro2", along +with a empty fourth one. In other words, the number of arguments is +adjusted so that every gate receives what it needs and nothing more. + +But haven't we said just above that a gate receives what the previous +one returns? Since "macro1" returns nothing, shouldn't "macro2" receive +four empty arguments? Things are actually a bit more complicated to +explain, but simpler to use: given two consecutive gates A and B, the +arguments passed to B are whatever A returns, plus additional arguments +taken from those that A received if necessary (i.e. if B takes more +arguments than A has returned). To put it differently, when a gate returns +*n* arguments, they simply replace the first *n* arguments of the current +arguments, which are passed to the next gate. In our example, "macro1" +returned nothing, so the arguments that it received were restored and +passed to "macro2". Here is another example: + + \gates list {mylist} [3] + \gates def {macro1} [2] {% + \immediate\write16{1: #1. 2: #2.}% + \gates return1 {ONE}} + \gates def {macro2} [4] {% + \immediate\write16{1: #1. 2: #2. 3: #3. 4: #4.}% + \gates return {first}{second}{third}{fourth}} + \gates def {macro3} [4] {% + \immediate\write16{1: #1. 2: #2. 3: #3. 4: #4.}} + \gates add {macro1, macro2, macro3} {mylist} + \gates execute {mylist} {one}{two}{three} + +And this prints: + + 1: one. 2: two. + 1: ONE. 2: two. 3: three. 4: . + 1: first. 2: second. 3: third. 4: fourth. + +It works as follows: "macro1" uses the first two arguments from "mylist", +and returns one; "macro2" thus takes that argument, plus the second, +third and fourth original arguments (the fourth being empty, since +"mylist" takes only three); four argument are returned, which is all +"macro3" needs, so there is no empty argument. + +As said above, l-gates automatically return what their last gates return; +more accurately, an l-gate declared with *n* arguments returns the same +number of arguments, those being taken from what the last gate returns +plus additional arguments restored as we've just seen. In our example, +"mylist" will return "first", "second" and "third" (it takes, and thus +returns, three arguments), because "macro3" returned nothing and the +arguments that it received from "macro2" are restored. (Actually, +returning doesn't mean anything here because "mylist" doesn't appear +in a l-gate itself.) + +This behavior is quite convenient, because it means that when a gate +doesn't modify an argument, it doesn't have to bother to return it. + + + + Controlling gates + ~~~~~~~~~~~~~~~~~ + +Here it is explained how to change a gate's conditions. + + -- Status -- :TeXStatus_tag + +The global status of a gate (i.e. its behavior wherever it is encountered) +can be set with the following actions: |open!|, |ajar!|, |skip!| and +|close!|, as in: + + \gates ajar! {mygate, myothergate} + +This also illustrates that you can set the status of several gates at +once, separating them with commas (spaces are ignored). + +The local status (i.e. the behavior of a gate in a given l-gate) is +set with the same action without an exclamation mark, followed by the +l-gate where you want to specify the status: + + \gates open {mygate, myothergate}{mylist} + +But you can also use "before <gate>" and "after <gate>", meaning that +the status of all gates before (resp. after) <gate> in the l-gate will +be set accordingly. For instance: + + \gates close {before mygate}{mylist} + +As an example of a status-controlled gate, let's consider the problem +of automatically removing the indentation of a paragraph. The usual +solution is: + + \everypar={{\setbox0=\lastbox}} + +The \everypar token list is inserted every time TeX begins a paragraph; +assigning \lastbox to a box register removes it from the list under +construction: here the indentation box is thus removed from the paragraph; +the assignment is done in a group so box 0 remains unaffected. The +problem is that other resources might want to use \everypar; if the +exploitation of the token list is always so direct, one resource might +wipe another. With gates, the solution is to plant an l-gate in \everypar; +then you can add whatever gate to it without disturbing the other gates +possibly there too: + + \gates list {everypar} + \gates def {noindent}{{\setbox0=\lastbox}} + \gates add {noindent}{everypar} + \everypar={\gates execute {everypar}} + +But we were interested in status. As is, all paragraphs will be unindented, +because "noindent" will be executed for each paragraph. Instead, we +should close it with + + \gates close {noindent}{everypar} + +and situations triggering an unindented paragraph (for instance, a +section header) should call + + \gates ajar {noindent}{everypar} + +so that "noindent" will make its job once, and then close. + + + -- Conditional -- :TeXConditional_tag + +Status is useful, but it suffers one flaw: you have to set it yourself. +That's no problem when you're handling a local situation like the +previous one; but things might a be a bit more far-reaching. You might +want X to influence Y, and X and Y might not be related at all; also, +X might change more than once. + +For more flexible control, gates can depend on a |conditional|, for +instance (illustrating both global and local setting): + + \gates conditional! {mygate}{\ifSomething} + \gates conditional {gate1, gate2}{mylist}{\ifSomethingElse} + +Now, "mygate" will be executed only when \ifSomething is true, just +like "gate1" and "gate2" in "mylist" will be executed in "mylist" when +\ifSomethingElse is true. The situations where conditionals can be put +to use are countless. A preface in a book, for instance, will certainly +make some \ifSpecialChapter conditional true, and many details will +depend on that conditional: e.g. unnumbered section headings, page +number in roman numerals, etc. If those are implemented with gates, +setting \ifSpecialChapter as the conditional for those gates will make +them depend on the larger picture (you could use status too, but the +conditional is more powerful because many different things can depend +on it, not only gates). + +What constitutes a valid conditional? Technically, whatever fits texapi's +\ifexpression. Indeed, Gates is written with texapi, and conditionals +rely internally on \ifexpression. What then fits \ifexpression? First, +any traditional TeX conditional, no matter whether it's a primitive +like \ifhmode or \ifcat or a macro defined with \newif. Second, +argument-taking conditionals, i.e. macros working like + + \ifXXX {<true>}{<false>} + +can be used too (texapi itself defines a good deal of such conditionals). +In both cases, what should be set in |conditional| is the test itself, +not the <true> and <false> parts. For instance, with \ifSomething and +\ifSomethingElse above, the true branch and the "\else ... \fi" +continuation were left out of the picture. If you used, say, \ifnum, +you'd specify something like (note the necessary space): + + \gates conditional {mygate}{mylist}{\ifnum0=1 } + +With a texapi conditional you could use: + + \gates conditional {mygate}{mylist}{\ifstring{foo}{bar}} + +(Here of course we have specified stupid conditionals which are always +false; more useful examples can be found below). + +But \ifexpression (hence |conditional|) also allows using simple logical +operators: "&" means *and*, "|" means *or*, "-" means *not*, and +subexpressions can be created with braces. For instance, the following +two conditionals are equivalent and are true if the absolute value of +N is smaller than 100 (the space following an operator is ignored): + + \ifnum N > -100 & \ifnum N < 100 + -{\ifnum N < -99 | \ifnum N > 99 } + +Pretty complex expressions can thus be assigned to a gate's conditional. +But things are better yet: conditionals take arguments, the same as the +gates they control, so that the test can depend not only on external +conditions but on what a gate receives. For instance, with: + + \gates conditional {mygate}{mylist}{\ifnum#1>0 } + +"mygate" will be executed only when its first argument is positive. +Another example: + + \gates conditional! {mygate}{-\ifstring{#1}{#2}} + +Here, "mygate" will be executed only when its first two arguments differ, +no matter in which list "mygate" appear, since the global conditional +is set here. + +If you don't want a gate to depend on a conditional anymore, simply +declare something like: + + \gates conditional! {mygate}{\iftrue} + + + -- Loop -- :TeXLoop_tag + +Third type of control: the execution of a gate can be repeated as long +as some condition is true. To do so, you use the |loop| action (with +an exclamation mark if you want to set the global loop). It takes the +same stuff as |conditional|, and the gate's arguments are passed to it +too. If the material evaluates to true, the gate is executed, then the +material is reevaluated, and if it is true again the gate is executed +again, and so on and so forth, so obviously something must happen so +that the iteration stops. + +When a gate repeats the arguments it returns are passed back to +itself, and before that to the loop. Here is an example: + + \gates def {myloop} [1] {% + \immediate\write16{\the\numexpr#1}% + \gates return {#1+1}} + \gates loop! {myloop} {\ifnum\numexpr#1<5 } + \gates execute {myloop}{1} + +This will print numbers from 1 to 4. Once 4 is printed, "myloop" returns +5 ("1+1+1+1+1", really), so the conditional controlling the loop isn't +true anymore and the iteration stops. If this happened in an l-gate, +the return value of "myloop" would be passed to the next gate. + +When a gate shouldn't be controlled by a loop anymore, you can use +|noloop| (with or without an exclamation mark): + + \gates noloop! {mygate} + + + -- Loop until -- :TeXLoopUntil_tag + +You can specify another type of loop with |loopuntil|: it works like +|loop|, except the gate is repeated as long as the material evaluates +to false. Also, the evaluation takes place after the gate is executed, +which means that the gate is always executed at least once; arguments +are repeatedly passed to the gate and the conditional as with |loop|, +except |loopuntil| always receives the return values of the gate it +controls (whereas on the first evaluation |loop| uses the arguments +passed to gate, not what it returns). Here's the previous gate rewritten +with |loopuntil|; it will work slightly differently: + + \gates def {myloop} [1] {% + \immediate\write16{\the\numexpr#1}% + \gates return {#1+1}} + \gates loopuntil! {myloop} {\ifnum\numexpr#1>4 } + \gates execute {myloop}{1} + +(The difference with the previous version is that here "myloop" will be +executed at least once, even if a number larger than 4 is passed to it. +Also, it is important to use "\ifnum\numexpr#1>4" and not "\ifnum\numexpr#1=5"; +the latter will work if "myloop" is always executed with a number smaller +than 5, but it will enter an infinite loop otherwise, since the condition +will never be true.) + +If you want a gate to stop being controlled by |loopuntil|, use +|noloopuntil|. + +Note that if both |loop| and |loopuntil| are specified, the latter is +ignored. + + + + The shorthand notation :TeXShorthand_tag + ~~~~~~~~~~~~~~~~~~~~~~ + +The shorthand notation allows you to define gates (e.g. l-gates with +subgates), including conditions, in one go, without having to call any +gate actions. This makes complex code much more readable. + +We've already seen the implicit call to |execute| by using a gate's +name instead of an action. You can also define m- and l- gates more +directly as follows: + + \gates def {mygate} [2] {...} + \gates list {mylist} [2] + +are equivalent to + + \gates [mygate] [2] {...} + \gates (mylist) [2] + +and the gate's name can be surrounded by space, it is trimmed away. (The +examples in this section all use this shorthand, but they would be still +valid with the explicit |def| and |list| instead.) Second, following the +number of arguments (or the gate's name if the gate takes no argument), +you can optionally specify global conditions by using "?" followed by a +list of "key = value" pairs, where the keys are "status", "conditional", +"loop" and "loopuntil"; for "status", the value should be one of "open", +"ajar", "skip" or "close", and for the other conditions it should be a +conditional as seen above. For instance, here's the creation of an m-gate +with status "close" and some loop: + + \gates [mygate] [2] ?{status = close, + loop = \ifnum#1<5 } {...} + +This is equivalent to: + + \gates [mygate] [2] {...} + \gates close! {mygate} + \gates loop! {mygate}{\ifnum#1<5 } + +Note that the key is trimmed, as is the value if the key is "status", +so space can be used to make things readable. In the case of a conditional +or loop's value, it isn't trimmed, because space can be important, as +in our example here where it delimits the number "5". The space on the +left is insignificant, though. + +And here comes the most interesting shorthand: when declaring an l-gate, +you can define and add subgates at once by declaring them just after the +l-gate (and its optional number of arguments and/or conditions, if any), +using the same notations as with implicit definitions: "[mygate]" or +"(mylist)", optionally followed by the number of arguments and/or the +conditions, and with a definition in case of an m-gate. The only difference +is that the conditions thus specified, if any, are the local ones relative +to the l-gate under construction. In other words, the following: + + \gates (mylist) [2] + [mygate] [2] ?{status = ajar} {...} + (myotherlist) [1] ?{loop = \ifnum#1<5 } + +is equivalent to the much more verbose + + \gates (mylist) [2] + \gates [mygate] [2] {...} + \gates (myotherlist) [1] + \gates add {mygate, myotherlist}{mylist} + \gates ajar {mygate}{mylist} + \gates loop {myotherlist}{mylist}{\ifnum#1<5 } + +The shorthand notation allows you to see at once that "mylist" contains +"mygate" and "myotherlist". But then, the latter is an l-gate too, so +it could be nice if we could add subgates to it in the same way; +well, we can, it suffices to use a dot: + + \gates (mylist) [2] + [mygate] [2] ?{status = ajar} {...} + (myotherlist) [1] ?{loop = \ifnum#1<5 } + . [mysubgate] [1] ?{...} {...} + +Here "mysubgate" will be added to "myotherlist" instead of "mylist", and +the conditions, if any, will be the local conditions relative to +"myotherlist", not "mylist". Now what if "myotherlist" contains an l-gate +"mysublist" and you want to add subgates to that one? Well, you use two +dots, and so on and so forth (I leave argument numbers and conditions +aside to make things simpler): + + \gates (mylist) + [mygate] {...} + (myotherlist) + . [mysubgate] {...} + . (mysublist) + . . [mysubsubgate] {...} + . . (yetanotherlist) + . . . (pleasestop) + +etc., etc. The notation with dots work as follows: if we assume that +gates are added to l-gates of a given level, then dots denote that level: +no dot means that the gate is added to the l-gate of level 0, i.e. the +gate defined with |list| ("mylist" here), and is itself of level 1, one +dot means that the gate is added to an l-gate of level 1 and is itself +of level 2 (e.g. "mysubgate" added to "myotherlist"), and so on and so +forth. Space around the dot is insignificant. The only (obvious) restriction +is that an l-gate at level *n* isn't available anymore if followed by a +gate at the same level or lower. This works a bit like a table of contents: +a subsection can't be added if there isn't a section immediately above +it. If you want to come back to a gate at another level, just use less +dots. For instance, if the code above continued with + + . . [gateX] {...} + [gateY] {...} + +then "gateX" and "gateY" would be defined and added to "mysublist" and +"mylist" respectively. Of course, after "gateX", "yetanotherlist" and +"pleasestop" aren't available for insertion anymore; similarly, after +"gateY", only "mylist" can host incoming gates, until a new l-gate is +declared. + +It's nice to define subgates and add them to an l-gate at once, but +sometimes you might want to add a gate that already exists. Using the +above notation would redefine it. Of course, you can still |add| and +specify the position, but that's not very convenient. So, besides +parentheses and square brackets, you can use "<mygate>" to add "mygate" +without redefining it; you can actually add several gates at once, by +separating them with commas. Local conditions can then be set as above. +For instance: + + \gates (mylist) + [gateA] {...} + <gateB, gateC> ?{status = close} + (myotherlist) + . <gateD> + +creates l-gate "mylist" with subgates "gateA", "gateB", "gateC" and +"myotherlist", "gateB" and "gateC" being locally closed, and "myotherlist" +containing "gateD". This of course requires that "gateB", "gateC" and +"gateD" already exist. If a gate thus added is an l-gate, the dot notation +can be used to add subgates to it. + +The dot is actually only the default character to signal subgates. You +can use others by declaring them with |subchar|, as in: + + \gates subchar # + +The character thus declared do not replace existing ones (this would be +dangerous) but simply adds a new possibility. For obvious reason, the +character can't be "[", "(", "<" or "?". + + + + Gate families :TeXFamilies_tag + ~~~~~~~~~~~~~ + +New gate families can be created with the |new| action. + + \gates new \MyGates {MyFamily} + +Now \MyGates will work exactly like \gates, except that when it manipulates +gates, they will all have the prefix "MyFamily:" attached to them. For +instance: + + \gates def {mygate}{...} + \MyGates def {mygate}{...} + +defines two different gates, "gates:mygate" and "MyFamily:mygate". + +If a family is explicitly given in a gate's name, the family +associated with the calling command isn't added. The following two +lines are equivalent, for instance: + + \gates def {MyFamily:mygate}{...} + \MyGates def {mygate}{...} + +Of course the shorthand notation allows you to mix families: + + \MyGates (MyList) + (MySubList) + . [MyGate] {...} + . [AnotherFamily:AnotherGate] {...} + +The family associated with a command can be queried with the |family| +action. For instance: + + \MyGates family + +returns "MyFamily". + + + + + If you get lost :TeXLost_tag + ~~~~~~~~~~~~~~~ + +Both |type| and |status| return a number: |type| returns 0 if there is +no gate with that name, 1 if it is an m-gate and 2 for an l-gate; +|status| returns 0 for a non-existing gate, and 1 to 4 for "open", +"ajar", "skip" and "close" respectively; |status| can also be used +with an exclamation mark to query global status: + + \gates status {mygate}{mylist} + \gates status! {mygate} + +The |subgates| action takes a definition as its second argument, which +will be executed with "#1" replaced with the subgate's name. For instance, +the following shows all the subgates in "mylist", with their types: + + \gates subgates {mylist}{% + \immediate\write16{% + #1 \ifnum\gates type {#1}=1 + (m-gate)\else (l-gate)\fi}% + } + +The |show| action can be used to display a gate's construction and +conditions. It is displayed as the shorthand notation. The |trace| +action takes a number and works as follows: if the number is 0, gates +of the associated families aren't traced; if it is 1, it is mentionned +if they are called (in an l-gate) and executed or not, and why; with +2, it works like 1, except the arguments passed to the gates are shown +too. + + + +Reference manual for TeX :TeXReference_tag +======================== + +All actions are called with \gates, or any other calling command created +with |new|, as follows: + +> \gates <action><space> + Executes <action>; if there is a gate called <action>, this is + equivalent to "\gates execute {<action>}". The name of the action (or + gate) is delimited by a space; depending on <action>, arguments might + be required. + +In what follows, <gate list> denotes a comma-separated list of gates, +e.g. "mygate, myothergate, thirdgate"; <gate spec> denotes either a +<gate list> or a relative position of the form "before <gate>" or +"after <gate>" + +> add <gate list>[<position>]<l-gate> + This adds all the gates in <gate list> to <l-gate>. If <position> is + missing, the addition occurs at the end of the l-gate. Otherwise, it + should be one of the following: "first", meaning the gates will be + added at the beginning of the l-gate; "last", meaning they will be + added at the end (so this is similar to no <position>); "before <name>", + and the addition will occur before gate <name> in the l-gate (it + should of course exist); "after <name>", and the addition will occur + after gate <name>. Note that <name> can be "first" or "last", denoting + the first and last gates in <l-gate> ("before first" and "after last" + are obviously synonymous with "first" and "last"). + +> ajar <gate spec><l-gate> + Sets the local status in <l-gate> to "ajar" for the gates in <gate spec>. + +> ajar! <gate spec> + Sets the global status for the gates in <gate spec> to "ajar". + +> close <gate spec><l-gate> + Sets the local status in <l-gate> to "close" for the gates in <gate spec>. + +> close! <gate spec> + Sets the global status for the gates in <gate spec> to "close". + +> conditional <gate spec><l-gate><conditional> + Sets the local conditional in <l-gate> to <conditional> for the gates + in <gate spec>. The conditional should be anything that fits texapi's + \ifexpression. + +> conditional! <gate spec><conditional> + Sets the global conditional for the gates in <gate spec> to <conditional>. + +> copy <gate1><gate2> + Defines <gate1> as <gate2> (either an m- or l-gate). The current global + status is also copied. If <gate2> is an l-gate, its gate list is also + copied, along with the current status of the gates it contains. On the + other hand, if <gate2> occurs in some l-gate(s), this information isn't + copied. + +> def <name>[<number of arguments>]<optional conditions>{<definition>} + Defines <name> as an m-gate with the given number of arguments and + definition. Such an assignment is always global. The number of arguments, + ranging from 0 to 9, is optional, in which case the gate takes no + argument. In other words, the following lines are equivalent: + + \gates def {mygate}{<definition>} + \gates def {mygate}[0]{<definition>} + + Global conditions can be specified with "?" followed by a key-value + list, where a key is one of "status", "conditional", "loop" and + "loopuntil" and the value is whatever fits the condition, as when + explicitely calling the associated action with an exclamation mark. + For instance, + + \gates def {mygate} ?{conditional = \ifsomething} {...} + + is equivalent to + + \gates def {mygate}{...} + \gates conditional! {mygate}{\ifsomething} + + In the key-value list, the key is always trimmed of surrounding spaces, + as is the value if the key is "status"; values aren't trimmed for + other conditions, because space might be significant (e.g. with + \ifnum), but leading space is harmless (it is ignored by the internal + processing of the conditional). + + Giving an gate's name between brackets instead of an action is similar + to using |def| with that gate: + + \gates [mygate] [1] {...} + +> edef <name>[<number of arguments>]<optional conditions>{<definition>} + Similar to def, but performs \edef. + +> execute <name><arguments> + Executes gate <name>. The number of <arguments> should match what + <name> was defined with. If <name> is an m-gate, this is but a + convoluted way of calling a macro. If <name> is an l-gate, this will + launch sub-gates. Not that the execution depends on the gate's global + conditions. Using a gate's name as an action is similar to using + |execute| with that gate. + +> family + Returns the family associated with the calling command. + +> list <name>[<number of arguments>]<optional conditions><optional subgates> + Defines <name> as an l-gate with the given number of arguments (can + be missing if 0). The optional global conditions can be specified + with "?" followed by a key-value list; see |def| above. The + <optional subgates> constitute the shorthand notation explained + above [see TeXShorthand_tag], defining and adding subgates to the + l-gate under construction, and specifying the local conditions too. + + Giving an gate's name between parentheses instead of an action is + similar to using |def| with that gate: + + \gates (mylist) [2] ?{status = ajar} + + +> loop <gate spec><l-gate><conditional> + Sets the local while-loop in <l-gate> to <conditional> for the gates + in <gate spec>. The conditional is the same as with |conditional|; + the difference is that the gates will be executed again as long as + the conditional is true, so there'd better be something somewhere + which makes it false. + +> loop! <gate spec><conditional>. + Sets the global while-loop for the gates in <gate spec> to <conditional>. + +> loopuntil <gate spec><l-gate><conditional> + Sets the local until-loop in <l-gate> to <conditional> for the gates + in <gate spec>. The conditional is the same as with |conditional|; + the gates will be executed until the conditional is true; this means + they will be executed at least once. If |loop| is also specified, + |loopuntil| is ignored. + +> loopuntil! <gate spec><conditional>. + Sets the global until-loop for the gates in <gate spec> to <conditional>. + +> new <control sequence><family> + Defines <control sequence> as a calling command for gates, associated + with <family>. + +> noloop <gate spec><l-gate><conditional> + Unsets the local while-loop in <l-gate> to <conditional> for the gates + in <gate spec>. + +> noloop! <gate spec> + Unsets the global while-loop for the gates in <gate spec>. + +> noloopuntil <gate spec><l-gate><conditional> + Unsets the local until-loop in <l-gate> to <conditional> for the gates + in <gate spec>. + +> noloopuntil! <gate spec> + Unsets the global until-loop for the gates in <gate spec>. + +> open <gate spec><l-gate> + Sets the local status in <l-gate> to "open" for the gates in <gate spec>. + +> open! <gate spec> + Sets the global status for the gates in <gate spec> to "open". + +> remove <gate list><l-gate> + Removes the gates in <gate list> (names separated by commas) in <l-gate>. + +> return <arguments> + In an m-gate, pass <arguments> to the next one. There should be as + many <arguments> as the gate was declared with. Any material following + the statement in the gate's definition will be gobbled. + +> return0, return1, return2 ... return8, return9 + Makes the gate return the specified number of arguments, no matter + the number of arguments the gate was defined with. + +> return! <arguments> + Makes the gate return an indefinite number of arguments. If <arguments> + is empty, it is similar to 'return0'; if <arguments> contains one + argument, it is similar to 'return1'; if it contains two, it is similar + to 'return2', etc. An argument is defined as usual in TeX: a token, + or balanced text. For instance, in what follows the second and third + lines are equivalent; the first does the same job if and only if called + inside a gate defined with three arguments (otherwise chaos will ensue). + + \gates return {one}{two}{three} + \gates return3 {one}{two}{three} + \gates return! {{one}{two}{three}} + +> show <gate> + Writes to the log and terminal <gate>'s type, its number of arguments, + global status, conditional, loops (if specified), and recursively its + subgates if <gate> is an l-gate (showing the local conditions). + +> skip <gate spec><l-gate> + Sets the local status in <l-gate> to "skip" for the gates in <gate spec>. + +> skip! <gate spec> + Sets the global status for the gates in <gate spec> to "skip". + +> status <gate><l-gate> + Returns the local status of <gate> in <l-gate>: 1 if the gate is open, + 2 if it is ajar, 3 if it is to be skipped, and 4 if it is closed; if + there is no <gate> in <l-gate> for whatever reason (including if there + exists no such l-gate), returns 0. + +> status! <gate> + Returns the global status of <gate>. + +> subchar <character> + Defines <character> as denoting a subgate in the shorthand notation. + "[", "(", "<" and "?" are forbidden. + +> subgates <l-gate><definition> + Executes <definition> with each gate in <l-gate>. In <definition>, + "#1" stands for the name of the subgate on each iteration. + +> type <name> + Returns 0 if <name> is not a gate, 1 if it is an m-gate and 2 if it + is an l-gate. + +> trace <number> + If <number> is 0, gate operations aren't reported; if 1, encountered + gates are reported, along with their status and conditional when + necessary; if 2, arguments passed to gates that are executed are also + displayed. Tracing affects only gates of the family associated with + the calling command. + + + + + +Gates in Lua :LuaUser_tag +============ + +In general, gates behave similarly in TeX and Lua, but the syntax +obviously differs. Also, there is one situation where both implementations +diverge: when arguments are returned. + + + + Loading and using gates + ~~~~~~~~~~~~~~~~~~~~~~~ + +Gates in Lua aren't loaded automatically with the gates package. So one +of the following should be issued somewhere: + + dofile("gates.lua") + require("gates.lua") + +Actually "dofile" requires more precision, e.g. (in LuaTeX): + + dofile(kpse.find_file("gates.lua")) + +On the other hand, "require" is a little bit more clever (in LuaTeX, +it uses "kpathsea"). Note that even if "dofile" is used, "gates.lua" +won't be loaded twice, because the file returns at once if "gates" +already exists. + +In the above paragraphs, `in LuaTeX' was mentioned twice: that's because +Lua gates can be used in any Lua interpreter. Lua is obviously required, +but not Lua*TeX*. In other words, Lua Gates don't rely on any special +feature of Lua in LuaTeX (libraries, in particular, aren't used). + +The file "gates.lua" returns nothing when loaded; it simply creates the +"gates" table, in which everything takes place until a |new| one is +created with a family. This means that all actions are fields of the +"gates" table. + + + + Definition and execution :LuaDefinition_tag + ~~~~~~~~~~~~~~~~~~~~~~~~ + +Here's how to define and execute gates in Lua. + + + -- Defining gates -- + +The |def| action creates an m-gate; it takes a table with (for now) two +entries: the entry at index 1 is the gate's name, the entry at index 2 +is the function performed by the gate. For instance, we can translate +our simple TeX example in Lua (since the table is the only argument to +|def|, the surrounding parentheses can be removed): + + gates.def {"add", function (n) return n + 3 end} + gates.def {"multiply", function (n) return n *2 end} + gates.def {"print", print} + +The function can be either an anonymous function created on the fly, +or a function variable, as with "print". However, such a syntax is +cumbersome in Lua, so you can directly assign to an entry in the "gates" +table, provided it hasn't the same name as an action (so it is impossible +with the "add" gate, for instance): + + function gates.multiply (n) + return n * 2 + end + +Actually, entries in the "gates" table be assigned any type; if a +function is assigned, a gate is created; otherwise, "gates" behaves as +an ordinary table with an ordinary entry: + + gates.mystring = "Hello" + print(gates.mystring) + +However, the entry can't be redefined as a gate anymore; if it is +assigned a function, it will be nothing more. In the following code, +"bar" is a gate, but "foo" isn't. + + gates.bar = function () ... end + gates.foo = "hello" + gates.foo = function () ... end + +But let's get back to proper gates. We want to add our m-gates to an +l-gate, which we declare beforehand with |list|: + + gates.list {"operation"} + +Like |def|, |list| takes a table as its single argument; the only +required field is the gate's name at index 1. We can now add the m-gates +to the l-gate: + + gates.add ({"add", "multiply", "print"}, "operation") + +The |add| action takes a table containing subgates as its first argument, +and a string representing the l-gate where the insertion is done as its +second. If you add a single subgate, the first argument can be a string. +More on |add| below [see LuaAddWithPosition_tag]. + + + -- Executing gates -- + +We can now execute our l-gate: + + gates.execute ("operation", 4) + +and "14" will be printed on the terminal. The |execute| action takes a +gate as its first arguments, and then the arguments that are to be +passed to the gate. Again, this syntax is a bit cumbersome, and gates +can be executed more naturally as: + + gates.execute.operation(4) + +or simpler, but provided the gate doesn't share a name with an action: + + gates.operation(4) + +The last two variants also let you retrieve the gate itself as a function, +instead of calling it: + + x = gates.execute.operation + callback.register("process_input_buffer", gates.operation) + + + :LuaAddWithPosition_tag +Now we can define another m-gate and add it to "operation", specifying +the position: + + gates.substract = function (n) return n-3 end + gates.add ("substract", "operation", "after multiply") + +And now "operation" returns 11 when fed 4. This was done by simply +adding an optional third argument to the |add| operation; this specifies +where the new gate(s) should be added in the l-gate: by default, it is +the end of the list, but you can say "first" to put the gate(s) at the +beginning or "before <name>" or "after <name>" to make the insertion +before or after the gate called <name>; instead of a name, you can also +use "first" or "last" to denote the first and last gates of the list, +so here we could have used "before last" instead of "after multiply". + +Finally, you can also |remove| a gate: + + gates.remove("substract", "operation") + +and now "operation" is what it was before. + + + -- Handling arguments -- + +When a gate is called, it may take arguments; in the case of an +l-gate, those arguments are passed to each subgates, one after the +other. Unlike gates in TeX, though, if a gate expects more arguments +than passed to the l-gate it belongs too, no empty argument is added. +For instance: + + gates.mygate = function (a, b) + print ("First: " .. tostring(a), + "Second: " .. tostring(b)) + end + gates.list {"mylist"} + gates.add ("mygate", "mylist") + gates.mylist("one") + +will print: + + First: one Second: nil + +Also, there is by default no `common argument pool', as there was in +TeX: a gate (in an l-gate) receives what the previous one returns and +nothing else; in other words if l-gate L receives four arguments and +subgate A returns only three, then subgate B will receive three +arguments. Consequently, all gates should return properly if arguments +are to be passed. + +But that is only default behavior. Lua gates can be made to work like +TeX gates (to some extent), by using |autoreturn|: + + gates.autoreturn ("mygate", true) + gates.autoreturn ("mygate", "mylist", true) + +When |autoreturn| is set for a gate (either globally, as in the first +line, or locally, as in the second), missing arguments will be restored +when the gate returns. To qualify as a missing argument, the following +conditions should hold: first, the value is "nil" (i.e. either "nil" +was explictly returned, or nothing was returned); second, no real +argument follows. For instance, suppose "mygate" is defined as follows: + + gates.mygate = function (A, B, C, D) + return nil, X + end + +If |autoreturn| is false, the following gate will receive "nil" and +"X"; if it is true, the arguments will be restored to "nil", "X", "C" +and "D"; the first argument isn't restored, even though it is "nil", +because it is followed by real argument "X". + +L-gates automatically returns whatever their last gate returns. Also, +unlike the TeX implementation (but in line with the Lua language), when +a gate returns outside an l-gate, the returned values can be used, as +with any other function. + + + + Controlling gates + ~~~~~~~~~~~~~~~~~ + +Here it is explained how gates can be controlled externally. + + + -- Status -- :LuaStatus_tag + +A gate's status can be set with the |open|, |ajar|, |skip| and +|close| actions; they take at least one argument, either a table: + + gates.open ({"mygate", "myothergate"}) + +or a simple string if only one gate is to be affected: + + gates.open ("mygate") + +With one argument, the actions set the global status; if a second +argument is present, it is the l-gate where the local status is to be +set: + + gates.ajar ({"mygate", "myothergate"}, "mylist") + +In this case, the first argument can denote a relative position of the +form "before <gate>" or "after <gate>": + + gates.ajar ("before mygate", "mylist") + + + -- Conditional -- :LuaConditional_tag + +To make a gate depends on an external state of affair rather than status +only, |conditional| can be used. The syntax is the same as for status, +except a second (in case of global conditional) or third (in case of +local conditional) argument is given. This should be a function, and +the gate's execution depends on what the function returns: the gate is +executed the function returns nothing or "false". The arguments that +are to be passed to the gate are passed beforehand to the conditional +function. For instance, if a gate receives arguments "A" and "B", and +its conditional function is "ControlFunction", then the gate's execution +can be schematized as: + + if ControlFunction(A, B) then + -- Execute gate + end + +If you don't want a gate to depend on a conditional anymore, you can +declare something like: + + gates.conditional ("mygate", "mylist", + function () return true end) + + + -- Loop -- :LuaLoop_tag + +The |loop| action allows a gate to be repeated; the syntax is the same +as for |conditional|, and the loop will be repeated as long as the +function evaluates to true (as with a "while" loop). Also, the gate's +arguments are repeatedly passed to the loop conditional. To delete a +gate's loop, use |loop| with "nil" as the third argument. As an +example, the following will print number from 1 to 4: + + function gates.mygate (n) + print(n) + return n + 1 + end + gates.loop("mygate", function (n) return n < 5 end) + gates.mygate(1) + + + -- Loop until -- :LuaLoopUntil_tag + +The |loopuntil| action is like |loop|, except the gate is repeated until +the conditional evaluates to false. Also, the conditional is evaluated +after the gate is executed, so the execution takes place at least once. +If both |loop| and |loopuntil| are set for a gate, the latter is ignored. + + + + + The shorthand notation :LuaShorthand_tag + ~~~~~~~~~~~~~~~~~~~~~~ + +Gates can be created and manipulated by actions, as we've done up to +now, but they can also be declared much faster. First, |def| and |list| +take tables as their arguments; that is for a good reason: entries +indexed with certain keys are equivalent to actions. The keys are +|autoreturn|, |status|, |conditional|, |loop| and |loopuntil| and setting +them when declaring a gate is like globally setting the associated +action (except there is a single action for |status|, which takes a +string as its value: "open", "ajar", "skip" or "close"). Thus the +following defines an m-gate with global status "ajar" and a |loop|: + + gates.def {"mygate", + status = "ajar", + loop = function (n) return n < 5 end, + function (n) + print(n) + return n + 1 + end} + +Note that the entries can be given in any order as long as the gate's +name is at index 1 and its definition at index 2. + +L-gates have another property: as said above, the entry at index 1 +should be the gate's name; but you can put tables representing gates +at index 2, 3, etc., and they represent the l-gate's subgates. Those +tables are the same as the ones passed to |def| and |list|; the only +difference is that setting of |autoreturn|, |status|, etc., is local +to the l-gate where they are added. The following code creates l-gate +"mylist" with subgates "mygate" and "myothergate", the former subject +to a local loop: + + gates.list {"mylist", + {"mygate", loop = function (n) return n < 5 end, + function (n) + print (n) + return n + 1 + end}, + {"myothergate", + function (n) + print("We're done!") + end}} + +Since the subtables are the same as the tables passed to |def| and +|list|, it means that an l-gate thus declared can host subgates too: + + gates.list {"mylist", + {"mysublist", + {"mygate", function () ... end}, + {"myothergate", function () ... end}}, + {"anothergate", function () ... end}} + +If you want to add an already existing gate, you can do so either by +using a simple string instead of a table, or (if you want to set |status| +and associates) a table with the name at index 1 and nothing at higher +indices: + + gates.list {"mylist", + "AnExistingSubgate", + {"AnExistingSubgateWithOption", status = "ajar"}} + +Note however that the second version is similar in form to an l-gate +defined without subgates; this means that you can't redefine an l-gate +thus, but the situations where you would like to do so (redefining an +existing l-gate, and redefining it without subgates, and in shorthand +notation) aren't many. In that case you should use |list| and |add| +instead. (On the other hand, if you do specify subgates, redefinition +will happen with an l-gate with that name already exists; in other +words, it won't be interpreted as inserting an existing l-gate and add +subgates to it.) + + + + Gate families :LuaFamilies_tag + ~~~~~~~~~~~~~ + +The |new| action takes a string (the family name) as its sole argument +an return a calling table: + + MyGates = gates.new ("MyFamily") + +"MyGates" can now be used like "gates" (to which is associated the +"gates" family), except "MyFamily" will be used as the family name when +necessary. + +A table's family is stored in the |family| entry; note that it is not +an action, hence not a function, but a string: + + print(MyGates.family) + + + + + If you get lost :LuaLost_tag + ~~~~~~~~~~~~~~~ + +Both |type| and |status| return a number: |type| returns 0 if there is +no gate with that name, 1 if it is an m-gate and 2 for an l-gate; +|status| returns 0 for a non-existing gate, and 1 to 4 for "open", +"ajar", "skip" and "close" respectively; |status| can also be used +with one or two arguments, indicating global or local status +respectively: + + gates.status ("mygate") + gates.status ("mygate", "mylist") + +The |subgates| action passes the names of all the subgates in a given +l-gate to a function. For instance, the following prints all +"mylist"'s subgates and their types: + + gates.subgates("mylist", + function (g) + local t + if gates.type(g) == 1 then + t = "(m-gate)" + else + t = "(l-gate)" + end + print (g, t) + end) + +The |show| action can be used to display a gate's construction and +conditions. It is displayed as the shorthand notation. The |trace| +action takes a number and works as follows: if the number is 0, gates +of the associated families aren't traced; if it is 1, it is mentionned +if they are called (in an l-gate) and executed or not, and why; with +2, it works like 1, except the arguments passed to the gates are shown +too. + + + + + + + + +Reference manual for Lua :LuaReference_tag +======================== + +There are some differences between gates in TeX and gates in Lua, owing to the +differences in syntax between the two languages. That said, the same +operations can be found in both interfaces. + +In what follows, <gate list> denotes either a table with gate names at +successive indices (e.g. "{'mygate', 'myothergate'}") or a single string +denoting a single gate.; <gate spec> denotes either a <gate list> or a +relative position of the form "before <gate>" or "after <gate>" (the +latter case only if an l-gate is also specified, obviously). + +> add (<gate list>, <l-gate>[, <position>]) + Adds the gates in <gate list> to <l-gate>. The optional <position> + should be one of the following: "first", meaning the gates will be + added at the beginning of the l-gate; "last", meaning they will be + added at the end (so this is similar to no <position>); "before <name>", + and the addition will occur before gate <name> in the l-gate (it + should of course exist); "after <name>", and the addition will occur + after gate <name>. Note that <name> can be "first" or "last", denoting + the first and last gates in <l-gate> ("before first" and "after last" + are obviously synonymous with "first" and "last"). + +> ajar (<gate spec>[, <l-gate>]) + Without <l-gate>, sets the global status for the gates in <gate spec> to + "ajar". With <l-gate>, sets the local status in <l-gate> to "ajar" for the + gates in <gate spec>. + +> autoreturn (<gate spec>[, <l-gate>], <boolean>) + Without <l-gate>, sets the global |autoreturn| for the gates in + <gate spec>; with <l-gate>, the local |autoreturn| is done. When a + gate's |autoreturn| is set, arguments will be restored if it returns + less than what it was passed. + +> close (<gate spec>[, <l-gate>]) + Without <l-gate>, sets the global status for the gates in <gate spec> to + "close". With <l-gate>, sets the local status in <l-gate> to "close" for the + gates in <gate spec>. + +> conditional (<gate spec>[, <l-gate>], <function>) + Without <l-gate>, sets the global conditional for the gates in + <gate spec> to <function>. With <l-gate>, sets the local conditional + in <l-gate> to <function> for the gates in <gate list>. That the + conditional is a function means that the gate(s) will be executed + only if <function> returns anything but "nil". + +> copy (<gate1>, <gate2>) + Defines <gate1> as <gate2> (either an m- or l-gate). The current global + status is also copied. If <gate2> is an l-gate, its gate list is also + copied, along with the current status of the gates it contains. On the + other hand, if <gate2> occurs in some l-gate(s), this information isn't + copied. + +> def (<table>) + Defines an m-gate whose name is the entry at index 1 in <table>, and + the defintion the function at index 2; |autoreturn|, |status|, + |conditional|, |loop| and |loopuntil| can also be used as key to + globally specify those settings. + + Assigning to an entry in the general "gates" table is a shorthand for + |def|; in other words, the following two lines are synonymous, provided + there isn't a gate action called "foo". + + gates.def {"foo", function (...) ... end} + gates.foo = function (...) ... end + + In the second case, if what is assigned isn't a function, then a new + entry is simply added to the "gates" table, as if it were a simple + table, unless the index is an existing action or gate (in which case + an error is raised). Note that this entry will then be unavailable to + host a gate if redefined. + +> execute (<gate>[, ...]) + Calls <gate> with the other arguments. There exists two shorthands: + "gates.<gate>(...)", provided <gate> doesn't clash with an action's + name, and "gates.execute.<gate>(...)". Those shorthands are also the + only way to retrieve the gate itself instead of executing it. + +> family + The family associated with a table created with |new|. This is a + string, not a function. + +> list (<table>) + Declares an l-gate whose name is the entry at index 1 in <table>. + Additional entries can be specified as with |def|. Tables at indices + 2, 3, etc., are subgates created and added at once to <table>, with + |status| and associates indicating local (not global) settings. + Existing gates can also be added without being redefined, by giving + their names (a simple string) instead of a full table, or a table + with nothing at indices 2 and higher. + +> loop (<gate spec>[, <l-gate>], <function>) + Without <l-gate>, sets the global while-loop for the gates in <gate spec> + to <function>. With <l-gate>, sets the local while-loop in <l-gate> + to <function> for the gates in <gate list>. The gates will be executed + again as long as <function> evaluates to true. To delete a gate's + while-loop, use the same action with "nil" as <function>. + +> loopuntil (<gate spec>[, <l-gate>], <function>) + Without <l-gate>, sets the global until-loop for the gates in <gate spec> + to <function>. With <l-gate>, sets the local until-loop in <l-gate> + to <function> for the gates in <gate list>. The gates will be executed + again as long as <function> doesn't evaluate to true. To delete a + gate's until-loop, use the same action with "nil" as <function>. + +> open (<gate spec>[, <l-gate>]) + Without <l-gate>, sets the global status for the gates in <gate spec> to + "open". With <l-gate>, sets the local status in <l-gate> to "open" for the + gates in <gate spec>. + +> remove (<gate list>, <l-gate>) + Removes the gates in <gate list> from <l-gate>. + +> show (<gate>) + Writes to the terminal (and log file, if in LuaTeX) <gate>'s type, + its number of arguments, global status, conditional, loops (if + specified), "autoreturn" if set to true, and recursively its subgates + if <gate> is an l-gate (showing the local settings). + +> skip (<gate spec>[, <l-gate>]) + Without <l-gate>, sets the global status for the gates in <gate spec> to + "skip". With <l-gate>, sets the local status in <l-gate> to "skip" for the + gates in <gate spec>. + +> status (<gate>[, <l-gate]) + Without <l-gate>, returns the global status of <gate>; with <l-gate>, + the local status is returned: 1 if the gate is open, 2 if it is ajar, + 3 if it is to be skipped, and 4 if it is closed; if there <gate> + doesn't exist, of doesn't exist in <l-gate>, 0 is returned. + +> subgates (<l-gate>, <function>) + Executes <function> with each gate (represented by its name as a + string) in <l-gate>. + +> trace (<number>) + If <number> is 0, gate operations aren't reported; if 1, encountered + gate are reported, along with their status and conditional when + necessary; if 2, arguments passed to gates that are executed are also + displayed. Tracing affects only gates of the family associated with + the calling command. + +> type (<name>) + Returns the type of <name>: 0 if it isn't a gate, 1 if it is an m-gate, 2 if + it is an l-gate. + + + + + diff --git a/Master/texmf-dist/doc/generic/gates/gates.tex b/Master/texmf-dist/doc/generic/gates/gates.tex new file mode 100755 index 00000000000..705a7243f54 --- /dev/null +++ b/Master/texmf-dist/doc/generic/gates/gates.tex @@ -0,0 +1,925 @@ +% This is the Gates package. +% Relevant information can be found in gates-doc.pdf +% (or gates-doc.txt in a text editor). +% +% Author: Paul Isambert. +% E-mail: zappathustra AT free DOT fr +% Comments and suggestions are welcome. +% Date: December 2011. + +\csname Come on, Gates is already loaded\endcsname +\expandafter\let\csname Come on, Gates is already loaded\endcsname\endinput +\input texapi +\setcatcodes{_@=11} + +\def\gates@error{\senderror{Gates}}% +\def\gates@error_nogate#1{\gates@error{`#1' isn't a gate}} +\def\gates@error_nogatelist#1{\gates@error{`#1' isn't a gate list}} +\def\gates@error_nogatein#1#2{\gates@error{No gate `#1' in list `#2'}} + +\def\gates@family_update#1{% + \def\gates@family_current{#1}% + } +\newstring{:} +\def\gates@family_check{% + \gates@family_pass\unbrace + } +\def\gates@family_pass#1#2{% + \passexpanded{\passtrim{#2}{\gates@family_passdo{#1}}}{\gates@family_current}% + } +\def\gates@family_passdo#1#2#3{% + \passexpanded{\ifcontains{:}}{#2} + {#1{#2}}{#1{#3:#2}}% + } +\def\gates@ifexists#1#2{% + \ifcs{gate \gates@family_check{#1}:#2}% + } + + +\def\gates@gates#1#2{% + \ifstring{#2}[ + {\gates@family_update{#1}% + \gates@gates_macro} + {\ifstring{#2}( + {\gates@family_update{#1}% + \gates@gates_list} + {\gates@gates_check{#1}}}#2% + } +\def\gates@gates_macro[#1]{% + \passtrim{#1}\gates@action@def + } +\def\gates@gates_list(#1){% + \passtrim{#1}\gates@action@list + } +\def\gates@gates_check#1#2 {% + \ifelseif{% + {\ifcs{gates@action@#2}} + {\gates@family_update{#1}\usecs{gates@action@#2}} + {\ifcs{gates@action@#2_withfamily}} + {\usecs{gates@action@#2_withfamily}{#1}} + {\ifcs{gate \gates@family_passdo\unbrace{#2}{#1}:code}} + {\gates@family_update{#1}\gates@action@execute{#2}} + \iftrue + {\gates@error{Action `\gates@family_passdo\unbrace{#2}{#1}' doesn't exist and there is no gate with that name}}}% + } + +\def\gates{% + \gates@gates{gates}% + } + +\def\gates@action#1{% + \long\defcs{gates@action@#1}% + } +\def\gates@action_withfamily#1{% + \long\defcs{gates@action@#1_withfamily}% + } + +\gates@action{new}#1#2{% + \def#1{% + \gates@gates{#2}% + }% + } + +\gates@action_withfamily{family}#1{#1} + +%%% +%%% The gate stack. +%%% +\def\gates@stack{} +\protected\def\gates@stack_push#1{% + \xdef\gates@current_gate{#1}% + \xdef\gates@stack{{#1}{\gates@stack}}% + } +\protected\def\gates@stack_pop{% + \expandafter\gates@stack_popit\gates@stack\gates@end + } +\def\gates@stack_popit#1#2\gates@end{% + \gdef\gates@stack{#2}% + \gates@stack_getcurrent#2{}\gates@end + } +\def\gates@stack_getcurrent#1#2\gates@end{% + \gdef\gates@current_gate{#1}% + } + +%%% +%%% Tracing and showing. +%%% +\def\gates@trace_value{0} +\gates@action{trace}#1{% + \defcs{gates@trace_value:\gates@family_current}{#1}% + } +\def\gates@trace_tab{} +\def\gates@trace_white{} +\def\gates@trace_addtab{% + \edef\gates@trace_tab{\gates@trace_tab. }% + \edef\gates@trace_white{\gates@trace_white\unbrace{ } }% + } +\def\gates@trace_untab{% + \passexpanded{\removeprefixin{. }}\gates@trace_tab\gates@trace_tab + \passexpanded{\passexpanded\removeprefixin{\unbrace{ } }}\gates@trace_white\gates@trace_white + } +\def\gates@trace_write#1#2{% + \iffcs{gates@trace_value:\gates@trace_getfamily#1\gates@terminator} + {\ifnum\usecs{gates@trace_value:\gates@trace_getfamily#1\gates@terminator}>0 + \immediate\write16{\gates@trace_white #2}% + \fi}} +\def\gates@trace_writetab#1#2{% + \iffcs{gates@trace_value:\gates@trace_getfamily#1\gates@terminator} + {\ifnum\usecs{gates@trace_value:\gates@trace_getfamily#1\gates@terminator}>0 + \immediate\write16{\gates@trace_tab #2}% + \fi}} +\def\gates@trace_getfamily#1:#2\gates@terminator{#1} + +\gates@action{show}#1{% + \immediate\write16{}% + \gates@family_pass\gates@show{#1}% + } +\def\gates@show#1{% + \ifcs{gate #1:list} + {\gates@show_write{#1 is an l-gate}{}{#1}% + \passexpandedcs{\gates@show_loop{#1}{. }}{gate #1:list}}% + {\ifcs{gate #1:code} + {\gates@show_write{#1 is an m-gate}{}{#1}}{\gates@error_nogate{#1}}}% + } +\newfor\gates@show_loop{2}#3,{% + \gates@show_write{#2#3}{#1_subgate_}{#3}% + \iffcs{gate #3:list}{\passexpandedcs{\gates@show_loop{#3}{#2. }}{gate #3:list}}% + } +\def\gates@show_write#1#2#3{% + \immediate\write16{#1 + \reverse\iffemptystring{#2}{(\ifcs{gate #3:list}lm-gate) }% + [\usecs{gate #3:args}] + (status: \ifcase\usecs{gate #2#3:status} + \or open\or ajar\or skip\or close\fi)% + \gates@show_condition{#2#3}{conditional}% + \gates@show_condition{#2#3}{loop}% + \gates@show_condition{#2#3}{loopuntil}% + }% + } + +\def\gates@show_condition#1#2{% + \iffcs{gate #1:#2} + { (#2: \expandafter\expandafter\expandafter\gates@show_conditionstrip + \expandafter\meaning\csname gate #1:#2\endcsname)}% + } +\def\gates@show_conditionstrip#1->{}% + + +%%% +%%% Checking conditions in gate definition. +%%% +\long\def\gates@conditions_get#1{% + \ifnextnospace? + {\gates@conditions_getlaunch{#1}} + {#1{{}{}{}{}}}% + } +\newstring{,} +\long\def\gates@conditions_getlaunch#1?#2{% + \ifsuffix,{#2} + {\gates@conditions_getloop{#1}{}{}{}{}{#2}} + {\gates@conditions_getloop{#1}{}{}{}{}{#2,}}% + } +\newfornoempty\gates@conditions_getloop{5}#6=#7,{% + \ifelseif{% + {\passtrim{#6}\ifstring{status}} {\passarguments{#1}{\trim{#7}}{#3}{#4}{#5}} + {\passtrim{#6}\ifstring{conditional}} {\passarguments{#1}{#2}{#7}{#4}{#5}} + {\passtrim{#6}\ifstring{loop}} {\passarguments{#1}{#2}{#3}{#7}{#5}} + {\passtrim{#6}\ifstring{loopuntil}} {\passarguments{#1}{#2}{#3}{#4}{#7}} + \iftrue {\gates@error{`#6' isn't a condition}}% + } + }[#1{{#2}{#3}{#4}{#5}}] +\long\def\gates@conditions_make#1#2#3#4#5{% + \gates@list_checkmaster + {\gates@action@add{#1}{\gates@list_master}% + \reverse\iffemptystring{#2}{\usecs{gates@action@#2}{#1}{\gates@list_master}}% + \reverse\iffemptystring{#3}{\gates@action@conditional{#1}{\gates@list_master}{#3}} + \reverse\iffemptystring{#4}{\gates@action@loop{#1}{\gates@list_master}{#4}} + \reverse\iffemptystring{#5}{\gates@action@loopuntil{#1}{\gates@list_master}{#5}}} + {\straightenif{ifnum}{\gates@list_subcount>0 } + {\gates@error{Wrong level of embedding for gate `#1'}}% + {\reverse\iffemptystring{#2}{\usecs{gates@action@#2!}{#1}}% + \reverse\iffemptystring{#3}{\usecs{gates@action@conditional!}{#1}{#3}} + \reverse\iffemptystring{#4}{\usecs{gates@action@loop!}{#1}{#4}}% + \reverse\iffemptystring{#5}{\usecs{gates@action@loopuntil!}{#1}{#5}}}}% + } + +%%% +%%% Defining list-gates. +%%% +\newcount\gates@list_subcount +\def\gates@list_masterlist{}% +\gates@action{list}#1{% + \ifnextnospace[ + {\gates@family_pass\gates@list_checkarg{#1}} + {\gates@family_pass\gates@list_checkarg{#1}[0]}% + } + +\def\gates@list_checkarg#1[#2]{% + \ifcs{gates@execute_get_#2_args} + {\gates@conditions_get{\gates@list_create{#1}{#2}}} + {\gates@list_create{#1}{0}{{}{}{}{}}[#2]}% + } + +\long\def\gates@list_create#1#2#3{% + \straightenif{ifnum}{#2<10 } + {\xdefcs{gate #1:args}{#2}% + \gdefcs{gate #1:status}{\gates@state_open}% + \gdefcs{gate #1:conditional}##1##2##3##4##5##6##7##8##9{\iftrue}% + \gdefcs{gate #1:list}{}% + \long\xdefcs{gate #1:code}##1##2##3##4##5##6##7##8##9{% + \ifcase#2 + \noexpand\passexpandedcs{\noexpand\gates@process{#1}{{}{}{}{}{}{}{}{}{}}}{gate #1:list}% + \or \noexpand\passexpandedcs{\noexpand\gates@process{#1}{{##1}{}{}{}{}{}{}{}{}}}{gate #1:list}% + \or \noexpand\passexpandedcs{\noexpand\gates@process{#1}{{##1}{##2}{}{}{}{}{}{}{}}}{gate #1:list}% + \or \noexpand\passexpandedcs{\noexpand\gates@process{#1}{{##1}{##2}{##3}{}{}{}{}{}{}}}{gate #1:list}% + \or \noexpand\passexpandedcs{\noexpand\gates@process{#1}{{##1}{##2}{##3}{##4}{}{}{}{}{}}}{gate #1:list}% + \or \noexpand\passexpandedcs{\noexpand\gates@process{#1}{{##1}{##2}{##3}{##4}{##5}{}{}{}{}}}{gate #1:list}% + \or \noexpand\passexpandedcs{\noexpand\gates@process{#1}{{##1}{##2}{##3}{##4}{##5}{##6}{}{}{}}}{gate #1:list}% + \or \noexpand\passexpandedcs{\noexpand\gates@process{#1}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}{}{}}}{gate #1:list}% + \or \noexpand\passexpandedcs{\noexpand\gates@process{#1}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}{}}}{gate #1:list}% + \or \noexpand\passexpandedcs{\noexpand\gates@process{#1}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}{##9}}}{gate #1:list}% + \fi}% + \gates@conditions_make{#1}#3% + \eaddright\gates@list_masterlist{{#1}}% + \gates@list_checknext} + {\gates@error{Gates can't take more than 9 arguments}}% + } + +\def\gates@list_checknext{% + \gates@list_subcount=1 + \skipspace{\futurelet\gates@list_nextchar\gates@list_checknextchar}% + } + +\def\gates@list_checknextchar{% + \expandafter\gates@list_checknextcharloop\expandafter{\gates@list_charlist}% + } + +\def\gates@list_charlist{.} +\gates@action{subchar}#1{% + \ifexpression{% + \if\noexpand#1[% + | \if\noexpand#1(% + | \if\noexpand#1<% + | \if\noexpand#1?} + {\gates@error{You can't use `#1' as a continuation char}} + {\eaddright\gates@list_charlist{\string#1}}% + } + +\newfor\gates@list_checknextcharloop#1{% + \if\noexpand\gates@list_nextchar\noexpand#1% + \advance\gates@list_subcount1 + \afterfi{\breakfor{\gobbleoneand{% + \skipspace{\futurelet\gates@list_nextchar\gates@list_checknextchar}}}}% + \fi + }[\gates@list_getsub] + +\def\gates@list_getsub{% + \ifnext[ + {\gates@list_getsubdef} + {\ifnext( + {\gates@list_getsublist} + {\ifnext< + {\gates@list_getsubgate} + {\gates@list_subcount=0 + \def\gates@list_masterlist{}}}}% + } + +\def\gates@list_getsublist(#1){% + \gates@action@list{#1}% + } + +\def\gates@list_getsubdef[#1]{% + \gates@def\gdefcs{#1}% + } + +\def\gates@list_getsubgate<#1>{% + \gates@conditions_get{\gates@list_dosubgate{#1}}% + } + +\def\gates@list_dosubgate#1#2{% + \gates@list_subgateloop#2{}{#1,}% + } + +\newfornoempty\gates@list_subgateloop{5}#6,{% + \gates@ifexists{#6}{code} + {\gates@family_pass\gates@conditions_make{#6}{#1}{#2}{#3}{#4}% + \passarguments{#1}{#2}{#3}{#4}{#6}} + {\gates@error_nogate{\gates@family_check{#6}}}% + }[\gates@ifexists{#5}{list} + {\eaddright\gates@list_masterlist{{\gates@family_check{#5}}}}{}% + \gates@list_checknext] + +\def\gates@list_checkmaster{% + \let\gates@list_master\gates@HopefullyThisRemainsUndefined + \passexpanded{\gates@list_getmaster{}{1}}\gates@list_masterlist + } + +\newfor\gates@list_getmaster{2}#3{% + \ifnum\gates@list_subcount=\numexpr#2\relax + \def\gates@list_master{#3}% + \afterfi{\breakfor{\def\gates@list_masterlist{#1{#3}}\firstoftwo}}% + \else + \afterfi{\passarguments{#1{#3}}{#2+1}}% + \fi + }[\def\gates@list_masterlist{#1}\secondoftwo]% + + +%%% +%%% Defining macro-gates. +%%% +\gates@action{def}{\gates@def\gdefcs} +\gates@action{edef}{\gates@def\xdefcs} + +\def\gates@def#1#2{% + \ifnextnospace[ + {\gates@family_pass{\gates@def_getargs{#1}}{#2}} + {\gates@family_pass{\gates@def_getargs{#1}}{#2}[0]}% + } + +\def\gates@def_getargs#1#2[#3]{% + \gates@conditions_get{\gates@def_prepare{#1}{#2}{#3}}% + } + +\long\def\gates@def_prepare#1#2#3#4#5{% + \ifelseif{% Can't use \ifcase, because #5 might contain an unbalanced conditional. + {\ifnum#3=0 } {\gates@def_do#1{#2}{#3}{}{}{#5}{#4}} + {\ifnum#3=1 } {\gates@def_do#1{#2}{#3}{##1}{{##1}}{#5}{#4}} + {\ifnum#3=2 } {\gates@def_do#1{#2}{#3}{##1##2}{{##1}{##2}}{#5}{#4}} + {\ifnum#3=3 } {\gates@def_do#1{#2}{#3}{##1##2##3}{{##1}{##2}{##3}}{#5}{#4}} + {\ifnum#3=4 } {\gates@def_do#1{#2}{#3}{##1##2##3##4}{{##1}{##2}{##3}{##4}}{#5}{#4}} + {\ifnum#3=5 } {\gates@def_do#1{#2}{#3}{##1##2##3##4##5}{{##1}{##2}{##3}{##4}{##5}}{#5}{#4}} + {\ifnum#3=6 } {\gates@def_do#1{#2}{#3}{##1##2##3##4##5##6}{{##1}{##2}{##3}{##4}{##5}{##6}}{#5}{#4}} + {\ifnum#3=7 } {\gates@def_do#1{#2}{#3}{##1##2##3##4##5##6##7}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}}{#5}{#4}} + {\ifnum#3=8 } {\gates@def_do#1{#2}{#3}{##1##2##3##4##5##6##7##8}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}}{#5}{#4}} + {\ifnum#3=9 } {\gates@def_do#1{#2}{#3}{##1##2##3##4##5##6##7##8##9}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}{##9}}{#5}{#4}} + {\iftrue} {\gates@error{Wrong number of arguments}}}% + } + +% #1: \gdef or \xdef +% #2: gate's name +% #3: number of arguments +% #4: parameter text (e.g., ##1, ##1##2, etc.) for internal code +% #5: replacement text (e.g., {##1}, {##1}{##2}, etc.) for internal code +% #6: definition of internal code +% #7: conditions +\long\def\gates@def_do#1#2#3#4#5#6#7{% + \xdefcs{gate #2:args}{#3}% + \gdefcs{gate #2:status}{\gates@state_open}% + \gdefcs{gate #2:conditional}##1##2##3##4##5##6##7##8##9{\iftrue}% + \long#1{gate #2:code}##1##2##3##4##5##6##7##8##9{% + \iffcs{gates@trace_value:\gates@trace_getfamily#2\gates@terminator} + {\ifnum\usecs{gates@trace_value:\gates@trace_getfamily#2\gates@terminator}=2 + \gates@trace_args{1}{#2}{#5}% + \fi}% + \usecs{gate #2:innercode}#5}% + \long#1{gate #2:innercode}#4{#6}% + \gates@conditions_make{#2}#7% + \reverse\iffemptycommand\gates@list_masterlist{\gates@list_checknext}% + } + +\newfor\gates@trace_args{2}#3{% + \gates@trace_write{#2}{\string##\the\numexpr#1<-\unexpanded{#3}}% + \passarguments{#1+1}{#2}% + } + +\gates@action_withfamily{type}#1#2{% + \ifcs{gate \gates@family_passdo\unbrace{#2}{#1}:code} + {\ifcs{gate \gates@family_passdo\unbrace{#2}{#1}:list}{2}{1}} + {0}% + } + +%%% +%%% Copying gates. +%%% +\gates@action{copy}#1#2{% + \gates@family_pass{\gates@family_pass\gates@copy_do{#1}}{#2}% + } +\def\gates@copy_do#1#2{% + \gates@ifexists{#2}{code} + {\global\letcstocs{gate #1:args}{gate #2:args}% + \gdefcs{gate #1:status}{\gates@state_open}% Better than to copy the gate's status. + \global\letcstocs{gate #1:conditional}{gate #2:conditional}% + \global\letcstocs{gate #1:loop}{gate #2:loop}% + \global\letcstocs{gate #1:loopuntil}{gate #2:loopuntil}% + \global\letcstocs{gate #1:code}{gate #2:code} + \gates@ifexists{#2}{list} + {\global\letcstocs{gate #1:list}{gate #2:list}% + \passexpandedcs{\gates@let_list{#1}{#2}}{gate #2:list}} + {\global\letcstocs{gate #1:internalcode}{gate #2:internalcode}}}% + {\gates@error_nogate{#2}}% + } + +\newfor\gates@let_list{2}#3,{% + \global\letcstocs{gate #1_subgate_#3:status}{gate #2_subgate_#3:status}% + \global\letcstocs{gate #1_subgate_#3:conditional}{gate #2_subgate_#3:conditional}% + \global\letcstocs{gate #1_subgate_#3:loop}{gate #2_subgate_#3:loop}% + \global\letcstocs{gate #1_subgate_#3:loopuntil}{gate #2_subgate_#3:loopuntil}% + } + +%%% +%%% Executing gates. +%%% +\def\gates@ifinlist{% + \ifdefined\gates@current_gate + \ifcsname gate \gates@current_gate:list\endcsname + \expandafter\expandafter\expandafter\firstoftwo + \else + \expandafter\expandafter\expandafter\secondoftwo + \fi + \else + \expandafter\secondoftwo + \fi + } +\gates@action{execute}#1{% + \gates@family_pass\gates@execute{#1}% + } +\def\gates@execute#1{% + \gates@ifexists{#1}{code} + {\gates@ifinlist + {\usecs{gates@execute_get_9_args}{#1}} + {\usecs{gates@execute_get_\usecs{gate #1:args}_args}{#1}}} + {\gates@error_nogate{#1}}% + } +\long\defcs{gates@execute_get_0_args}#1{% + \gates@execute_do{#1}{{}{}{}{}{}{}{}{}{}}% + } +\long\defcs{gates@execute_get_1_args}#1#2{% + \gates@execute_do{#1}{{#2}{}{}{}{}{}{}{}{}}% + } +\long\defcs{gates@execute_get_2_args}#1#2#3{% + \gates@execute_do{#1}{{#2}{#3}{}{}{}{}{}{}{}}% + } +\long\defcs{gates@execute_get_3_args}#1#2#3#4{% + \gates@execute_do{#1}{{#2}{#3}{#4}{}{}{}{}{}{}}% + } +\long\defcs{gates@execute_get_4_args}#1#2#3#4#5{% + \gates@execute_do{#1}{{#2}{#3}{#4}{#5}{}{}{}{}{}}% + } +\long\defcs{gates@execute_get_5_args}#1#2#3#4#5#6{% + \gates@execute_do{#1}{{#2}{#3}{#4}{#5}{#6}{}{}{}{}}% + } +\long\defcs{gates@execute_get_6_args}#1#2#3#4#5#6#7{% + \gates@execute_do{#1}{{#2}{#3}{#4}{#5}{#6}{#7}{}{}{}}% + } +\long\defcs{gates@execute_get_7_args}#1#2#3#4#5#6#7#8{% + \gates@execute_do{#1}{{#2}{#3}{#4}{#5}{#6}{#7}{#8}{}{}}% + } +\long\defcs{gates@execute_get_8_args}#1#2#3#4#5#6#7#8#9{% + \gates@execute_do{#1}{{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}{}}% + } +\long\defcs{gates@execute_get_9_args}#1#2#3#4#5#6#7#8#9{% + \gates@execute_get_nine_arg{#1}{{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}}% + } +\long\def\gates@execute_get_nine_arg#1#2#3{% + \gates@execute_do{#1}{#2{#3}}% + } + +\long\def\gates@execute_do#1#2{% + \ifcase\usecs{gate #1:status} + \or % open + \gates@processtrue + \def\gates@trace_status{}% + \or % ajar + \gates@processtrue + \gdefcs{gate #1:status}{\gates@state_close}% + \def\gates@trace_status{ (ajar -> close)}% + \or % skip + \gates@processfalse + \gdefcs{gate #1:status}{\gates@state_open}% + \def\gates@trace_status{ (skip -> open)}% + \or % close + \gates@processfalse + \def\gates@trace_status{ (close)}% + \fi +% + \iffemptycommand\gates@trace_tab{\gates@trace_write{#1}{}}% +% + \edef\gates@process_tempconditional{\primunexpanded\expandafter\expandafter\expandafter{\csname gate #1:conditional\endcsname#2}}% + \ifexpression{\ifgates@process & \expandafter\ifexpression\expandafter{\gates@process_tempconditional}} + {\gates@trace_write{#1}{Executing \ifcs{gate #1:list}{l-}{m-}gate #1\gates@trace_status}% + \gates@stack_push{#1}% + \gates@execute_launchloop{#1}{#2}} + {\gates@trace_write{#1}{Ignoring \ifcs{gate #1:list}{l-}{m-}gate #1\gates@trace_status + \expandafter\reverse\expandafter\iffexpression{\gates@process_tempconditional}{ (False conditional)}}% + \gates@ifinlist{\gates@execute_backtolist{#2}}{}}% + } + +\long\def\gates@execute_launchloop#1#2{% + \iffcs{gate #1:list}\gates@trace_addtab + \ifcs{gate #1:loop} + {\gates@execute_loop{#1}{#2}} + {\usecs{gate #1:code}#2\gates@execute_stoploop{#1}{#2}}% + } +\long\def\gates@execute_loop#1#2{% + \expandafter\expandafter\expandafter\ifexpression + \expandafter\expandafter\expandafter{\csname gate #1:loop\endcsname#2} + {\usecs{gate #1:code}#2\gates@execute_stoploop{#1}{#2}}% + {\gates@execute_end{#2}}% + } +\long\def\gates@execute_loopuntil#1#2{% + \expandafter\expandafter\expandafter\ifexpression + \expandafter\expandafter\expandafter{\csname gate #1:loopuntil\endcsname#2} + {\gates@execute_end{#2}}% + {\usecs{gate #1:code}#2\gates@execute_stoploop{#1}{#2}}% + } +\long\def\gates@execute_stoploop#1#2{% + \ifcs{gate #1:loop} + {\gates@execute_loop{#1}{#2}} + {\ifcs{gate #1:loopuntil} + {\gates@execute_loopuntil{#1}{#2}} + {\gates@execute_end{#2}}}% + } +\long\def\gates@execute_end#1{% + \iffcs{gate \gates@current_gate:list}\gates@trace_untab + \gates@stack_pop + \iffcs{gate \gates@current_gate:list} + {\gates@execute_backtolist{#1}}% + } +\long\def\gates@execute_backtolist#1#2#3{% + \gates@process_stoploop{#2}{#3}{#1}{#2}{#3}% + } + +\gates@action{return}{\usecs{gates@action@return\usecs{gate \gates@current_gate:args}}} +\gates@action{return!}#1{% + \gates@process_returncount#1{gates@end}{gates@end}{gates@end}{gates@end}{gates@end}{gates@end}{gates@end}{gates@end}{gates@end}\gates@reallytheend#1% + }% +\long\def\gates@process_returncount#1#2#3#4#5#6#7#8#9{% + \gates@process_returngobble{% + \ifelseif{% + {\ifstring{#1}{gates@end}} {\usecs{gates@action@return0}} + {\ifstring{#2}{gates@end}} {\usecs{gates@action@return1}} + {\ifstring{#3}{gates@end}} {\usecs{gates@action@return2}} + {\ifstring{#4}{gates@end}} {\usecs{gates@action@return3}} + {\ifstring{#5}{gates@end}} {\usecs{gates@action@return4}} + {\ifstring{#6}{gates@end}} {\usecs{gates@action@return5}} + {\ifstring{#7}{gates@end}} {\usecs{gates@action@return6}} + {\ifstring{#8}{gates@end}} {\usecs{gates@action@return7}} + {\ifstring{#9}{gates@end}} {\usecs{gates@action@return8}} + \ifrue {\usecs{gates@action@return9}}} + }% + } +\gates@action{return0}{% + \gates@return_pass{\unbrace{}}% + } +\gates@action{return1}#1{% + \gates@return_pass{\gobbleoneand{{#1}}}% + } +\gates@action{return2}#1#2{% + \gates@return_pass{\gobbletwoand{{#1}{#2}}}% + } +\gates@action{return3}#1#2#3{% + \gates@return_pass{\gobblethreeand{{#1}{#2}{#3}}}% + } +\gates@action{return4}#1#2#3#4{% + \gates@return_pass{\gobblefourand{{#1}{#2}{#3}{#4}}}% + } +\gates@action{return5}#1#2#3#4#5{% + \gates@return_pass{\gobblefiveand{{#1}{#2}{#3}{#4}{#5}}}% + } +\gates@action{return6}#1#2#3#4#5#6{% + \gates@return_pass{\gobblesixand{{#1}{#2}{#3}{#4}{#5}{#6}}}% + } +\gates@action{return7}#1#2#3#4#5#6#7{% + \gates@return_pass{\gobblesevenand{{#1}{#2}{#3}{#4}{#5}{#6}{#7}}}% + } +\gates@action{return8}#1#2#3#4#5#6#7#8{% + \gates@return_pass{\gobbleeightand{{#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}}}% + } +\gates@action{return9}#1#2#3#4#5#6#7#8#9{% + \gates@return_passnine{{#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}}% + } +\long\def\gates@return_pass#1#2\gates@execute_stoploop#3#4{% + \passexpanded{\gates@execute_stoploop{#3}}{#1#4}% + } +\long\def\gates@return_passnine#1#2\gates@execute_stoploop#3#4{% + \gates@execute_stoploop{#3}{#1}% + } + + +%%% +%%% Processing l-gates. +%%% +\newife\ifgates@process +\newfornoempty\gates@process{2}#3,{% + \ifcase\usecs{gate #1_subgate_#3:status} + \or % open + \gates@processtrue + \def\gates@trace_status{}% + \or % ajar + \gates@processtrue + \gdefcs{gate #1_subgate_#3:status}{\gates@state_close}% + \def\gates@trace_status{ (ajar -> close)}% + \or % skip + \gates@processfalse + \gdefcs{gate #1_subgate_#3:status}{\gates@state_open}% + \def\gates@trace_status{ (skip -> open)}% + \or % close + \gates@processfalse + \def\gates@trace_status{ (close)}% + \fi + \edef\gates@process_tempconditional{\primunexpanded\expandafter\expandafter\expandafter{\csname gate #1_subgate_#3:conditional\endcsname#2}}% + \ifexpression{\ifgates@process & \expandafter\ifexpression\expandafter{\gates@process_tempconditional}} + {\gates@trace_writetab{#1}{Calling subgate #3\gates@trace_status}% + \gates@process_launchloop{#1}{#3}{#2}{#1}{#3}}% + {\gates@trace_writetab{#1}{Ignoring subgate #3\gates@trace_status + \expandafter\ifexpression\expandafter{\gates@process_tempconditional}{}{ (False conditional)}}}% + }[\usecs{gates@action@return\usecs{gate #1:args}}#2] + +\def\gates@process_launchloop#1#2#3{% + \ifcs{gate #1_subgate_#2:loop} + {\gates@process_loop{#1}{#2}{#3}} + {\gates@action@execute{#2}#3}% + } +\def\gates@process_loop#1#2#3{% + \expandafter\expandafter\expandafter\ifexpression + \expandafter\expandafter\expandafter{\csname gate #1_subgate_#2:loop\endcsname#3} + {\gates@action@execute{#2}#3} + {\gates@process_nextgate{#3}}% + } +\def\gates@process_loopuntil#1#2#3{% + \expandafter\expandafter\expandafter\ifexpression + \expandafter\expandafter\expandafter{\csname gate #1_subgate_#2:loopuntil\endcsname#3} + {\gates@process_nextgate{#3}}% + {\gates@action@execute{#2}#3} + } +\def\gates@process_stoploop#1#2#3{% + \ifcs{gate #1_subgate_#2:loop} + {\gates@process_loop{#1}{#2}{#3}} + {\ifcs{gate #1_subgate_#2:loopuntil} + {\gates@process_loopuntil{#1}{#2}{#3}} + {\gates@process_nextgate{#3}}}% + } +\long\def\gates@process_nextgate#1#2{% + \passarguments{#2}{#1}% + } + + +\gates@action{subgates}#1{% + \gates@family_pass{\def\gates@subgates_gate}{#1}% + \afterassignment\gates@subgates_create + \def\gates@subgates_macro##1% + } + +\def\gates@subgates_create{% + \ifcs{gate \gates@subgates_gate:list} + {\passexpandedcs{\gates@subgates_process{\gates@subgates_macro}}{gate \gates@subgates_gate:list}} + {\gates@error_nogatelist{\gates@subgates_gate}}% + } + +\newfor\gates@subgates_process{1}#2,{% + #1{#2}% + } + +%%% +%%% Managing gate lists. +%%% +\gates@action{add}#1{% + \ifnextnospace[ + {\gates@add{#1}} + {\gates@add{#1}[]}% + } + +\newstring{before } \newstring{after } +\def\gates@add#1[#2]#3{% + \gates@family_pass{\gates@add_prepare{#1}{#2}}{#3}% + } +\def\gates@add_prepare#1#2#3{% + \ifcs{gate #3:list} + {\let\gates@add_prefix\gates@somethingtotallyundefined + \let\gates@add_suffix\gates@somethingtotallyundefined +% + \ifelseif{% + {\ifexpression{ \ifemptystring{#2} | \ifstring{#2}{last} | \ifstring{#2}{after last} }} + {\passexpandedcs\gates@add_setends{gate #3:list}{}} + {\ifexpression{ \ifstring{#2}{first} | \ifstring{#2}{before first} }} + {\passexpandedcs{\gates@add_setends{}}{gate #3:list}} + {\ifprefix{before }{#2}} + {\removeprefixand{before }{#2}{\passexpandedcs\gates@add_split{gate #3:list}{0}{#3}}} + {\ifprefix{after }{#2}} + {\removeprefixand{after }{#2}{\passexpandedcs\gates@add_split{gate #3:list}{1}{#3}}} + \iftrue + {\gates@error{Unknown position: `#2'}}}% +% + \ifdefined\gates@add_prefix + \gates@add_do{#3}{#1,}% + \xdefcs{gate #3:list}{\gates@add_prefix\gates@add_suffix}% + \fi}% +% + {\gates@error_nogatelist{#3}}% + } + +\def\gates@add_setends#1#2{% + \def\gates@add_prefix{#1}% + \def\gates@add_suffix{#2}% + } + +\def\gates@add_split#1#2#3#4{% + \ifelseif{% + {\ifstring{#4}{first}}% "before first" has been dealt with, so this is "after first". + {\splitstringat{,}{#1}\gates@add_setends + \addright\gates@add_prefix{,}} + {\ifstring{#4}{last}}% Same thing, the other way around: "before last". Harder. + {\let\gates@add_prefix\emptycs + \def\gates@add_suffix{#1}% + \dowhile{\expandafter\gates@add_iftwo\gates@add_suffix,,\gates@end} + {\passexpanded{\splitstringat{,}}{\gates@add_suffix}\gates@add_setends + \addright\gates@add_prefix{,}}} + \iftrue + {\gates@family_pass{\gates@add_splitat{#1}{#2}{#3}}{#4}}}% + } +\def\gates@add_splitat#1#2#3#4{% + \ifelseif{% + {\ifprefix{#4,}{#1}} + {\ifcase#2 + \gates@add_setends{}{#1}% + \or + \def\gates@add_prefix{#4,}% + \removeprefixin{#4,}{#1}\gates@add_suffix + \fi} + {\ifcontains{,#4,}{#1}} + {\splitstringat{,#4,}{#1}\gates@add_setends + \ifcase#2 + \addright\gates@add_prefix{,}% + \addleft\gates@add_suffix{#4,}% + \or + \addright\gates@add_prefix{,#4,} + \fi} + \iftrue + {\gates@error_nogatein{#4}{#3}}}% + } + +\def\gates@add_iftwo#1,#2,#3\gates@end{% + \ifemptystring{#2}\secondoftwo\firstoftwo + } + +\newfornoempty\gates@add_do{1}#2,{% + \gates@ifexists{#2}{code} + {\eaddright\gates@add_prefix{\gates@family_check{#2},}% + \gdefcs{gate #1_subgate_\gates@family_check{#2}:status}{\gates@state_open}% + \gdefcs{gate #1_subgate_\gates@family_check{#2}:conditional}##1##2##3##4##5##6##7##8##9{\iftrue}} + {\gates@error_nogate{\gates@temp}}% + } + +\gates@action{remove}#1#2{% + \gates@family_pass{\gates@remove_launch{#1}}{#2}% + } +\def\gates@remove_launch#1#2{% + \ifcs{gate #2:list} + {\gates@remove{#2}{#1,}} + {\gates@error_nogate{#2}}% + } +\newfornoempty\gates@remove{1}#2,{% + \gates@family_pass{\gates@remove_do{#1}}{#2}% + } +\def\gates@remove_do#1#2{% + \passexpandedcs\gates@remove_check{gate #1:list}{#2} + {\global\letcs{gate #1_subgate_#2:status}\gates@thisisabsolutelyundefined + \global\letcs{gate #1_subgate_#2:conditional}\gates@thisisabsolutelyundefined + \global\letcs{gate #1_subgate_#2:loop}\gates@thisisabsolutelyundefined + \global\letcs{gate #1_subgate_#2:loopuntil}\gates@thisisabsolutelyundefined + \passexpandedcs{\gates@remove_loop{}{#1}{#2}}{gate #1:list}} + {\gates@error_nogatein{#2}{#1}}% + } +\def\gates@remove_check#1#2{% + \ifcontains{,#2,}{#1}% The first comma so it doesn't spot ...,xxx#2,... + {\firstoftwo} + {\ifprefix{#2,}{#1}\firstoftwo\secondoftwo}% + } +\newfor\gates@remove_loop{3}#4,{% + \reverse\iffstring{#3}{#4}{\passarguments{#1#4,}{#2}{#3}}% + }[\gdefcs{gate #2:list}{#1}] + +%%% +%%% opening, ajaring (it exists!), skipping and closing. +%%% +\chardef\gates@state_open = 1 +\chardef\gates@state_ajar = 2 +\chardef\gates@state_skip = 3 +\chardef\gates@state_close = 4 + +\gates@action_withfamily{status}#1#2#3{% + \ifcs{gate \gates@family_passdo\unbrace{#3}{#1}_subgate_\gates@family_passdo\unbrace{#2}{#1}:status} + {\passcs\the{gate \gates@family_passdo\unbrace{#3}{#1}_subgate_\gates@family_passdo\unbrace{#2}{#1}:status}} + {0}% + } + +\gates@action_withfamily{status!}#1#2{% + \ifcs{gate \gates@family_passdo\unbrace{#2}{#1}:status} + {\passcs\the{gate \gates@family_passdo\unbrace{#2}{#1}:status}} + {0}% + } + +\long\def\gates@status_change#1#2{% + \gates@family_pass{\gates@status_changedo{#1}}{#2}% + } +\def\gates@status_changedo#1#2#3#4{% + \ifcs{gate #2:list} + {\ifelseif{% + {\ifprefix{before }{#1}} {\passexpandedcs{\removeprefixand{before }{#1}{\gates@family_pass{\gates@switch_b{#3}{#2}}}{#4}}{gate #2:list}}% + {\ifprefix{after }{#1}} {\passexpandedcs{\removeprefixand{after }{#1}{\gates@family_pass{\gates@switch_a{#3}{#2}}}{#4}}{gate #2:list}}% + \iftrue {\gates@switch{#3}{#2}{#4}{#1,}}}} + {\gates@error_nogatelist{#2}}% + } + +\gates@action{open}#1#2{% + \gates@status_change{#1}{#2}{status}{\gates@state_open}% + } +\gates@action{open!}#1{% + \gates@switch_x{status}{\gates@state_open}{#1,}% + } + +\gates@action{ajar}#1#2{% + \gates@status_change{#1}{#2}{status}{\gates@state_ajar}% + } +\gates@action{ajar!}#1{% + \gates@switch_x{status}{\gates@state_ajar}{#1,}% + } + +\gates@action{skip}#1#2{% + \gates@status_change{#1}{#2}{status}{\gates@state_skip}% + } +\gates@action{skip!}#1{% + \gates@switch_x{status}{\gates@state_skip}{#1,}% + } + +\gates@action{close}#1#2{% + \gates@status_change{#1}{#2}{status}{\gates@state_close}% + } +\gates@action{close!}#1{% + \gates@switch_x{status}{\gates@state_close}{#1,}% + } + +\gates@action{conditional}#1#2#3{% + \gates@status_change{#1}{#2}{conditional}{#3}% + } +\gates@action{conditional!}#1#2{% + \gates@switch_x{conditional}{#2}{#1,}% + } + +\gates@action{loop}#1#2#3{% + \gates@status_change{#1}{#2}{loop}{#3}% + } +\gates@action{loop!}#1#2{% + \gates@switch_x{loop}{#2}{#1,}% + } + +\gates@action{noloop}#1#2{% + \gates@status_change{#1}{#2}{noloop}{}% + } +\gates@action{noloop!}#1{% + \gates@switch_x{noloop}{}{#1,}% + } + +\gates@action{loopuntil}#1#2#3{% + \gates@status_change{#1}{#2}{loopuntil}{#3}% + } +\gates@action{loopuntil!}#1#2{% + \gates@switch_x{loopuntil}{#2}{#1,}% + } + +\gates@action{noloopuntil}#1#2{% + \gates@status_change{#1}{#2}{noloopuntil}% + } +\gates@action{noloopuntil!}#1{% + \gates@switch_x{noloopuntil}{}{#1,}% + } + +\newfornoempty\gates@switch{3}#4,{% + \gates@family_pass{\gates@switch_do{#1}{#2}{#3}}{#4}% + } +\long\def\gates@switch_do#1#2#3#4{% + \ifcs{gate #2_subgate_#4:status} + {\gates@switch_evaluate{#2_subgate_#4}{#1}{#3}} + {\gates@error_nogatein{#4}{#2}}% + } +\newfornoempty\gates@switch_b{4}#5,{% + \ifstring{#3}{#5} + {\breakfor{}}% + {\gates@switch_evaluate{#2_subgate_#5}{#1}{#4}}% + } +\newfornoempty\gates@switch_a{4}#5,{% + \iffstring{#3}{#5} + {\retrieverest{\gates@switch{#1}{#2}{#4}}}% + }[\gates@error_nogatein{#3}{#2}] +\newfornoempty\gates@switch_x{2}#3,{% + \ifcs{gate \gates@family_check{#3}:status} + {\gates@switch_evaluate{\gates@family_check{#3}}{#1}{#2}} + {\gates@error_nogate{\gates@family_check{#3}}}% + } +\long\def\gates@switch_evaluate#1#2#3{% + \ifelseif{% + {\ifexpression{ \ifstring{#2}{conditional} | \ifstring{#2}{loop} | \ifstring{#2}{loopuntil} }} + {\gdefcs{gate #1:#2}##1##2##3##4##5##6##7##8##9{#3}} + {\ifstring{#2}{noloop}} + {\global\letcs{gate #1:loop}\gates@somethingtotallyundefined} + {\ifstring{#2}{noloopuntil}} + {\global\letcs{gate #1:loopuntil}\gates@somethingtotallyundefined} + \iftrue + {\gdefcs{gate #1:#2}{#3}}} + } + +\restorecatcodes +\endinput +% There was a time when writing this code I thought +% I could keep it below 200 lines. Then 300. Then I stopped +% counting. I'm cursed by loops. diff --git a/Master/texmf-dist/tex/generic/gates/gates.lua b/Master/texmf-dist/tex/generic/gates/gates.lua new file mode 100755 index 00000000000..d5ea2c4ae88 --- /dev/null +++ b/Master/texmf-dist/tex/generic/gates/gates.lua @@ -0,0 +1,695 @@ +-- This is the Gates package, Lua version. +-- Relevant information can be found in gates-doc.pdf +-- (or gates-doc.txt in a text editor). +-- +-- Author: Paul Isambert. +-- E-mail: zappathustra AT free DOT fr +-- Comments and suggestions are welcome. +-- Date: December 2011. + +if gates then + return +end + +local _insert, _remove = table.insert, table.remove +local _gsub, _match, _format = string.gsub, string.match, string.format +local _write_nl, print_error +if texio then + _write_nl, print_error = texio.write_nl, tex.error +else + function _write_nl (s) + print("\n" .. s) + end + print_error = error +end +local unpack = table.unpack or unpack + +local function _copy (tb) + local t = {} + for a, b in pairs (tb) do + if type(b) == "table" then + t[a] = _copy(b) + else + t[a] = b + end + end + return t +end + +-- Errors. +local function _error (...) + print_error("Lua-Gates error: " .. _format(...)) +end +local function _error_nogate (g) + _error("`%s' isn't a gate", g) +end +local function _error_nogatelist (g) + _error("`%s' isn't a gate list", g) +end +local function _error_nogatein (g, l) + _error("No gate `%s' in list `%s'", g, l) +end + +local function _check_family(fam, name) + return _match(name, ":") and name or fam .. ":" .. name +end + +-- Tracing and showing. +local function _write (...) + _write_nl(_format(...)) +end +local _tab, _white = "", "" +local function _addtab () + _tab = ". " .. _tab + _white = " " .. _white +end +local function _untab () + _tab = _gsub(_tab, ". ", "", 1) + _white = _gsub(_white, " ", "", 1) +end +local _trace_values = {} +local function trace (fam, n) + _trace_values[fam] = n +end +local function _trace_value (name) + return _trace_values[_match(name, "(.-):") or name] or 0 +end +local function _report (fam, s, ...) + if _trace_value(fam) > 0 then + if _white == "" then + _write(" ") + end + _write(_white .. s, ...) + end +end +local function _reporttab (fam, s, ...) + if _trace_value(fam) > 0 then + _write(_tab .. s, ...) + end +end + +local _rawgate +local function _getinfo (g) + local s = g.status == 1 and "open" + or g.status == 2 and "ajar" + or g.status == 3 and "skip" + or g.status == 4 and "close" + local c = tostring(g.conditional) + local l = g.loop and tostring(g.loop) + local u = g.loopuntil and tostring(g.loopuntil) + local a = g.autoreturn and " (autoreturn)" or "" + return s, c, l, u, a +end + +local function _show (gate, tab) + local g = _rawgate(gate.name) + local s, c, l, u, a = _getinfo(gate) + local info = _format(tab .. "%s (%s-gate) (status: %s) (conditional: %s)%s", + gate.name, g.list and "l" or "m", s, c, a) + if l then + info = _format(info .. " (loop: %s)", l) + end + if u then + info = _format(info .. " (loopuntil: %s)", u) + end + _write(info) + if g.list then + for _, G in ipairs(g.list) do + _show(G, tab .. ". ") + end + end +end + +local function show (fam, name) + name = _check_family(fam, name) + local g = _rawgate(name) + if g then + _show(g, "") + else + _error_nogate(name) + end +end + +local _open = 1 +local _ajar = 2 +local _skip = 3 +local _close = 4 +local _statuses = { + open = _open, + ajar = _ajar, + skip = _skip, + close = _close + } + +-- Some functions can take one argument or +-- multiple arguments in a table. +local function _totable (x) + return type(x) == "table" and x or {x} +end + +local _default = function (...) return ... end + +local execute = {[0] = {}} + +function _rawgate (name) -- Localized above. + return execute[0][name] +end + +local function _getmax (tb) + local max = 0 + for n in pairs (tb) do + max = n > max and n or max + end + return max +end +local function _restoreargs (arg, orarg, autoreturn) + if autoreturn then + local argmax, orargmax = _getmax(arg), _getmax(orarg) + for i = argmax + 1, orargmax do + arg[i] = orarg[i] + end + end + return arg +end + +execute.__index = function (_, name) + local f = _rawgate(name) + if f then + return function (...) + local s = "" + if f.status == _open or f.status == _ajar then + if f.status == _ajar then + s = " (ajar -> close)" + f.status = _close + end + if f.conditional(...) then + _report(name, "Executing %s-gate %s", f.list and "l" or "m", name, s) + if not f.list and _trace_value(name) > 1 then + local arg = _copy({...}) + for a, b in pairs(arg) do + arg[a] = b + for i = 1, a-1 do + arg[i] = arg[i] or "nil" + end + end + for a, b in ipairs(arg) do + b = tostring(b) + -- Can't use _write(), because it calls string.format(), + -- and b might contain literal %'s. + _write_nl(_white .. "#" .. a .. "<-" .. b) + end + end + local orarg, arg = {...}, {...} + if f.loop then + while f.loop(unpack(arg)) do + arg = {f.fct(unpack(arg))} + orarg = _restoreargs(arg, orarg, f.autoreturn) + end + return unpack(arg) + elseif f.loopuntil then + repeat + arg = {f.fct(unpack(arg))} + orarg = _restoreargs(arg, orarg, f.autoreturn) + until f.loopuntil(unpack(arg)) + return unpack(arg) + else + arg = {f.fct(unpack(arg))} + orarg = _restoreargs(arg, orarg, f.autoreturn) + return unpack(arg) + end + else + _report(name, "Ignoring %s-gate %s%s (False conditional)", f.list and "l" or "m", name, s) + end + else + if f.status == _skip then + s = " (skip -> open)" + f.status = _open + else + s = " (close)" + end + _report(name, "Ignoring %s-gate %s%s%s", f.list and "l" or "m", name, s, f.conditional() and "" or " (False conditional)") + return ... + end + end + else + _error_nogate(name) + return _default + end +end + +setmetatable(execute, execute) + +local function _evertrue () + return true +end + +local function add (fam, tb, list, where) + list = _check_family(fam, list) + local gatelist = _rawgate(list) + if gatelist and gatelist.list then + tb = _totable(tb) + local position + if where then + where = _match(where, "^%s*(.-)%s*$"):gsub("%s+", " ") + if where == "first" or where == "before first" then + position = 1 + elseif where == "after first" then + position = 2 + elseif where == "last" or where == "after last" then + position = #gatelist.list + 1 + elseif where == "before last" then + position = #gatelist.list + else + local prep, gate = _match(where, "^(.-)%s+(.-)$") + if prep and gate and (prep == "before" or prep == "after") then + gate = _check_family(fam, gate) + for n, g in ipairs(gatelist.list) do + if g.name == gate then + position = prep == "before" and n or n+1 + break + end + end + if not position then + _error_nogatein(gate, list) + return + end + else + _error("Unknown position: `%s'", where) + return + end + end + else + position = #gatelist.list + 1 + end + local _tb = {} + for _, x in ipairs(tb) do + _insert(_tb, 1, x) + end + tb = _tb + for _, name in ipairs(tb) do + name = _check_family(fam, name) + local macro = _rawgate(name) + if macro then + _insert(gatelist.list, position, { name = name, status = _open, conditional = _evertrue }) + else + _error_nogate(name) + end + end + else + _error_nogatelist(list) + end +end + +local function remove (fam, tb, list) + list = _check_family(fam, list) + local gatelist = _rawgate(list) + if gatelist and gatelist.list then + tb = _totable(tb) + for _, fct in ipairs(tb) do + fct = _check_family(fam, fct) + local n, found = 1 + while n <= #gatelist.list do + if gatelist.list[n].name == fct then + found = true + _remove(gatelist.list, n) + else + n = n + 1 + end + end + if not found then + _error_nogatein(fct, list) + end + end + else + _error_nogatelist(list) + end +end + +local function subgates (fam, list, fct) + list = _check_family(fam, list) + local gatelist = _rawgate(list) + if gatelist and gatelist.list then + for _, g in ipairs (gatelist.list) do + fct(g.name) + end + else + _error_nogatelist(list) + end +end + +local function _status (fam, fct, list, key, value) + if list then + list = _check_family(fam, list) + local gatelist = _rawgate(list) + if gatelist then + if type(fct) == "string" and (_match(fct, "^before ") or _match(fct, "^after ")) then + local prep, gate = _match(fct, "^(.-)%s+(.-)$") + if prep and gate and (prep == "before" or prep == "after") then + local list, position = gatelist.list + if gate == "first" then + position = 1 + elseif gate == "last" then + position = #list + else + gate = _check_family(fam, gate) + for n, g in ipairs(list) do + if g.name == gate then + position = n + break + end + end + end + if position then + if prep == "before" then + for n, g in ipairs(list) do + if n == position then + break + else + g[key] = value + end + end + else + for n, g in ipairs(list) do + if n > position then + g[key] = value + end + end + end + else + _error_nogatein(gate, list) + end + else + _error("Unknown position `%s'", fct) + end + else + fct = _totable(fct) + for _, func in ipairs(fct) do + func = _check_family(fam, func) + local found + for n, tb in ipairs(gatelist.list) do + if tb.name == func then + found = true + gatelist.list[n][key] = value + end + end + if not found then + _error_nogatein(func, list) + end + end + end + else + _error_nogatelist(list) + end + else + fct = _totable(fct) + for _, func in ipairs(fct) do + func = _check_family(fam, func) + local macro = _rawgate(func) + if macro then + macro[key] = value + else + _error_nogate(func) + end + end + end +end + +local function open (fam, fct, list) + _status(fam, fct, list, "status", _open) +end + +local function ajar (fam, fct, list) + _status(fam, fct, list, "status", _ajar) +end + +local function skip (fam, fct, list) + _status(fam, fct, list, "status", _skip) +end + +local function close (fam, fct, list) + _status(fam, fct, list, "status", _close) +end + +local function conditional (fam, fct, list, cond) + if type(list) == "string" then + _status(fam, fct, list, "conditional", cond) + else + _status(fam, fct, nil, "conditional", list) + end +end + +local function loop (fam, fct, list, cond) + if type(list) == "string" then + _status(fam, fct, list, "loop", cond) + else + _status(fam, fct, nil, "loop", list) + end +end + +local function loopuntil (fam, fct, list, cond) + if type(list) == "string" then + _status(fam, fct, list, "loopuntil", cond) + else + _status(fam, fct, nil, "loopuntil", list) + end +end + +local function autoreturn (fam, fct, list, cond) + if type(list) == "string" then + _status(fam, fct, list, "autoreturn", cond) + else + _status(fam, fct, nil, "autoreturn", list) + end +end + + +local function _store (tab) + local name = tab[1] + local fct = tab[2] + local tb = { + name = name, + status = _statuses[tab.status or "open"], + conditional = tab.conditional or _evertrue, + loop = tab.loop, + loopuntil = tab.loopuntil, + autoreturn = tab.autoreturn} + if not fct then + tb.list = {} + fct = function (...) + _addtab() + local orarg, arg = {...}, {...} + for n, func in ipairs(tb.list) do + local s = "" + if func.status == _open or func.status == _ajar then + if func.status == _ajar then + s = " (ajar -> close)" + func.status = _close + end + if func.conditional(unpack(arg)) then + _reporttab(func.name, "Calling subgate %s%s", func.name, s) + if func.loop then + while func.loop(unpack(arg)) do + arg = {execute[func.name](unpack(arg))} + orarg = _restoreargs(arg, orarg, func.autoreturn) + end + elseif func.loopuntil then + repeat + arg = {execute[func.name](unpack(arg))} + orarg = _restoreargs(arg, orarg, func.autoreturn) + until func.loopuntil(unpack(arg)) + else + arg = {execute[func.name](unpack(arg))} + orarg = _restoreargs(arg, orarg, func.autoreturn) + end + else + _reporttab(func.name, "Ignoring subgate %s%s (False conditional)", func.name, s) + end + else + if func.status == _skip then + s = " (skip -> open)" + tb.list[n].status = _open + else + s = " (close)" + end + _reporttab(func.name, "Ignoring subgate %s%s%s", func.name, s, func.conditional() and "" or " (False conditional)") + end + end + _untab() + return unpack(arg) + end + end + tb.fct = fct + execute[0][name] = tb +end + +local function def (fam, tab) + tab[1] = _check_family(fam, tab[1]) + _store(tab) +end + +local function list (fam, tab) + tab[1] = _check_family(fam, tab[1]) + local subgates = {} + for i = 2, #tab do + _insert(subgates, tab[i]) + tab[i] = nil + end + _store(tab) + for _, g in ipairs(subgates) do + g = type(g) == "string" and {g} or g + g[1] = _check_family(fam, g[1]) + if type(g[2]) == "function" then + _store({g[1], g[2]}) + elseif type(g[2]) == "table" then + local a = {} + for i = 2, #g do + _insert(a, g[i]) + end + list(fam, {g[1], unpack(a)}) + elseif not (_rawgate(g[1])) then + _store({g[1]}) + end + add(fam, g[1], tab[1]) + local s = g.status == "open" and _open + or g.status == "ajar" and _ajar + or g.status == "skip" and _skip + or g.status == "close" and _close + or _open + _status(fam, g[1], tab[1], "status", s) + local c = type(g.conditional) == "function" and g.conditional or _evertrue + _status(fam, g[1], tab[1], "conditional", c) + local l = type(g.loop) == "function" and g.loop + _status(fam, g[1], tab[1], "loop", l) + local L = type(g.loopuntil) == "function" and g.loopuntil + _status(fam, g[1], tab[1], "loopuntil", L) + _status(fam, g[1], tab[1], "autoreturn", g.autoreturn) + end +end + +local function copy (fam, name1, name2) + name1, name2 = _check_family(fam, name1), _check_family(fam, name2) + local base = _rawgate(name2) + if base then + if base.list then + _store(name1) + _rawgate(name1).list = _copy(base.list) + else + _store(name1, base.fct) + end + _status(fam, name1, nil, "status", _open) -- Better than to copy the gate's status. + _status(fam, name1, nil, "conditional", base.conditional) + _status(fam, name1, nil, "loop", base.loop) + _status(fam, name1, nil, "loopuntil", base.loopuntil) + else + _error_nogate(name2) + end +end + +local function Type (fam, name) + name = _check_family(fam, name) + local macro = _rawgate(name) + if macro then + return macro.list and 2 or 1 + else + return 0 + end +end + +local function status (fam, gate, list) + gate = _check_family(fam, gate) + list = list and _check_family(fam, list) + local fct = _rawgate(gate) + list = list and _rawgate(list) and _rawgate(list).list + if fct then + if list then + for _, g in ipairs(list) do + if g.name == gate then + return g.status + end + end + else + return fct.status + end + else + return 0 + end +end + +local _mt = { + trace = trace, + show = show, + def = def, + list = list, + copy = copy, + add = add, + remove = remove, + open = open, + ajar = ajar, + skip = skip, + close = close, + type = Type, + status = status, + conditional = conditional, + loop = loop, + loopuntil = loopuntil, + autoreturn = autoreturn, + subgates = subgates, +} + +local function new (_, fam) + local t = {family = fam} + setmetatable(t, _mt) + return t +end + +_mt.new = new + +_mt.__index = function (tb, name) + if name == "execute" then + return setmetatable({}, + {__call = + function (_, name, ...) + name = _check_family(tb.family, name) + return execute[name](...) + end, + __index = function (_, name) + name = _check_family(tb.family, name) + return execute[name] + end}) + elseif _mt[name] then + return function (...) return _mt[name](tb.family, ...) end + else + name = _check_family(tb.family, name) + local macro = _rawgate(name) + if macro then + return execute[name] +-- The following was removed because a gate instance can contain other +-- things than gates; for instance "gates.foo = 5" is valid. But one might +-- want to know whether an entry exists or not (e.g. "if gates.foo then" or +-- "if type(gates.foo) ~= 'nil' then"), in which case the next three lines +-- would produce an error (and return a function). +-- else +-- _error("`%s' isn't an action or a gate", name) +-- return _default + end + end +end + +_mt.__newindex = function (tb, key, value) + if _mt[key] then + _error("You can't assign to key `%s', it is an action", key) + else + if type(value) == "function" then + def(tb.family, {key, value}) + else + local name = _check_family(tb.family, key) + if _rawgate(name) then + _error("You can't assign to key `%s', it is already a gate", key) + else + rawset(tb, key, value) + end + end + end +end + +gates = new(nil, "gates") diff --git a/Master/texmf-dist/tex/generic/gates/gates.sty b/Master/texmf-dist/tex/generic/gates/gates.sty new file mode 100644 index 00000000000..28152eacb60 --- /dev/null +++ b/Master/texmf-dist/tex/generic/gates/gates.sty @@ -0,0 +1,14 @@ +% This is gates.sty, a style file to load +% the Gates package in LaTeX. Useful information +% can be found in gates-doc.pdf or gates-doc.txt. +% +% Author: Paul Isambert. +% E-mail: zappathustra AT free DOT fr +% Comments and suggestions are welcome. +% Date: December 2011. + +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{gates}[2011/12/03 v.0.1 Modular architecture.] +\chardef\formatnumber=4 +\input gates.tex +\endinput diff --git a/Master/texmf-dist/tex/generic/gates/t-gates.tex b/Master/texmf-dist/tex/generic/gates/t-gates.tex new file mode 100644 index 00000000000..0fb80e8d01b --- /dev/null +++ b/Master/texmf-dist/tex/generic/gates/t-gates.tex @@ -0,0 +1,24 @@ +% This is t-gates.tex, a third-party file to load +% the Gates package in ConTeXt. Useful information +% can be found in gates-doc.pdf or gates-doc.txt +% +% Author: Paul Isambert. +% E-mail: zappathustra AT free DOT fr +% Comments and suggestions are welcome. +% Date: December 2011. +% +%D \module +%D [ file=t-gates, +%D version=2011.12.03, +%D title=\CONTEXT\ User Module, +%D subtitle=Gates, +%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 / gates} +\chardef\formatnumber=3 +\input gates.tex +\endinput diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check index fb19b26290f..ded9dac0653 100755 --- a/Master/tlpkg/bin/tlpkg-ctan-check +++ b/Master/tlpkg/bin/tlpkg-ctan-check @@ -176,7 +176,7 @@ my @TLP_working = qw( free-math-font-survey frenchle frletter frontespizio ftcap ftnxtra fullblck fullwidth functan fundus fwlw - g-brief gaceta galois gamebook garrigues gastex gatech-thesis gauss + g-brief gaceta galois gamebook garrigues gastex gatech-thesis gates gauss gb4e gcard gchords gcite gene-logic genmisc genmpage gentium gentle geometry geometry-de german germbib germkorr getfiledate getoptk diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds index 1b75082de3a..1f068f3c7ad 100755 --- a/Master/tlpkg/libexec/ctan2tds +++ b/Master/tlpkg/libexec/ctan2tds @@ -824,6 +824,7 @@ chomp ($Build = `cd $Master/../Build/source && pwd`); 'timing', "die 'skipping, noinfo license, 1994 mf'", 'tipa', "&MAKEtipa", 'titlepage-uni-dortmund', "die 'skipping, needs graphics (too short names) in .'", + 'titleps', "die 'skipping, use titlesec'", 'tkhyph', "die 'skipping, part of hyph-utf8'", 'tksbrief', "die 'skipping, LaTeX 2.09'", 'tkz-base', "&MAKEtkz", @@ -1150,6 +1151,7 @@ $standardtex='\.(.bx|cfg|sty|clo|ldf|cls|def|fd|cmap|4ht)$'; 'fragments', '\.tex|' . $standardtex, 'frame', 'frame\.tex|' . $standardtex, 'frankenstein','\.(sto|stq)|' . $standardtex, + 'gates', '\.lua|t-gates|' . $standardtex, 'genmisc', '\.sty|\.tex', 'geometry', '\.sty|\.clo|\.ldf|\.cls|\.def|\.fd$', # not cfg 'geometry-de', 'NULL', # all doc diff --git a/Master/tlpkg/tlpsrc/collection-genericextra.tlpsrc b/Master/tlpkg/tlpsrc/collection-genericextra.tlpsrc index 3d48b6895c0..a0d6c789b26 100644 --- a/Master/tlpkg/tlpsrc/collection-genericextra.tlpsrc +++ b/Master/tlpkg/tlpsrc/collection-genericextra.tlpsrc @@ -17,6 +17,7 @@ depend epigram depend fenixpar depend fltpoint depend fntproof +depend gates depend ifetex depend iftex depend insbox diff --git a/Master/tlpkg/tlpsrc/gates.tlpsrc b/Master/tlpkg/tlpsrc/gates.tlpsrc new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/Master/tlpkg/tlpsrc/gates.tlpsrc |