Difference between revisions of "System Macros"

From Wiki
Jump to navigation Jump to search
m
 
(17 intermediate revisions by 10 users not shown)
Line 1: Line 1:
This is an evolving copy of the original article [http://tex.aanhet.net/context/syst-gen-doc.pdf ConTeXt System Macros, part 1: general macros] by [[User:Taco|Taco]].
+
== Introduction ==
 
 
=== Abstract ===
 
  
All large macro packages for TeX have the need for a number of low-level macros to easy the programming effort. This is definately true for the ConTeXt package, where the extensive use of key--value pairs and the multilingual interface introduce extra complications to the already tricky art of TeX programming.
+
All large macro packages for TeX have the need for a number of low-level macros to ease the programming effort. This is definitely true for the ConTeXt package, where the extensive use of key-value pairs and the multilingual interface introduce extra complications to the already tricky art of TeX programming.
  
Some of these internal macros are real gems, and nearly all of them can also be used in your own source documents. Although most of ConTeXt is described in the source code, sometimes the explanations are too technical for a casual user, and some of the documentation, especially the examples, is still in dutch.
+
Some of these internal macros are real gems, and nearly all of them can also be used in your own source documents. Although most of ConTeXt is described in the source code, sometimes the explanations are too technical for a casual user, and some of the documentation, especially the examples, is still in dutch.
  
This series of articles will try to highlight the most usable commands of the internal macro layer, using english examples, and removing most of the technical stuff (like the definitions of the macros themselves, and the optimization history).
+
The 'System Macros' documentation attempts to explain the most usable commands of the internal macro layer, using english examples to clarify usage.
  
 
Disclaimer: Quite a lot of the explanation text is copied from source code documentation by Hans Hagen. Always assume that the errors are mine and the good jokes are his.
 
Disclaimer: Quite a lot of the explanation text is copied from source code documentation by Hans Hagen. Always assume that the errors are mine and the good jokes are his.
  
__TOC__
+
The current text will mostly deal with the file [[source:syst-gen.mkii|syst-gen.mkii]] (see [http://repo.or.cz/w/context.git/blob/HEAD:/tex/context/base/syst-aux.mkiv syst-aux.mkiv] <!-- [[source:syst-aux.mkiv|syst-aux.mkiv]] --> for MkIV).  
 
+
Most of those macros are very basic, and many of them are related to programming constructs like flow control (<code>\if</code> statements), data structures (comma separated lists), and definitions.
== Introduction ==
 
 
 
This article will mostly deal with the file [[source:syst-gen.tex| syst-gen.tex]]. All except the first couple of macros appear in this file, which is input immediately after the inclusion of a stripped down version of the plain TeX format. Most of the following macros are very basic, most of them are related to programming constructs like flow control (<code>\if</code> statements), data structures (comma separated lists), and definitions.
 
 
 
[[System Macros/Fundamentals]]
 
 
 
[[System_Macros/Mnemonics_and_Aliases]]
 
 
 
[[System_Macros/Scratch_Variables]]
 
 
 
[[System_Macros/Expansion_Control]]
 
 
 
[[System_Macros/Handling_Arguments]]
 
 
 
[[System_Macros/Definitions_and_Assignments]]
 
 
 
[[System_Macros/Branches_and_Decisions]]
 
 
 
== Cases ==
 
 
 
ConTeXt makes extensive use of a sort of case or switch command. Depending of the presence of one or more provided items, some actions is taken. These macros can be nested without problems.
 
 
 
<texcode>
 
\processaction          [x]    [a=>\a,b=>\b,c=>\c]
 
\processfirstactioninset [x,y,z] [a=>\a,b=>\b,c=>\c]
 
\processallactionsinset  [x,y,z] [a=>\a,b=>\b,c=>\c]
 
</texcode>
 
 
 
This macro is most often used in the <code>key-value</code> parser, like in this (simplified) example, where the user has said width=small or something similar:
 
 
 
<texcode>
 
\processaction
 
    [\TESTwidth] % should expand to 'small'
 
    [small=>\message{small was chosen},
 
    medium=>\message{medium was chosen}]
 
</texcode>
 
 
 
You can supply both a <code>default</code> action and an action to be undertaken when an <code>unknown</code> value is met:
 
 
 
<texcode>
 
\processallactionsinset
 
    [x,y,z]
 
    [a=>\a,
 
    b=>\b,
 
    c=>\c,
 
    default=>\default,
 
    unknown=>\unknown{... \commalistelement ...}]
 
</texcode>
 
 
 
If the first argument is empty, this macro scans the list for the keyword <code>default</code> and executes the related action, if present. If the first argument is not empty but also not in the list, the action related to <code>unknown</code> is executed. Both keywords must be at the end of list <code>#2</code>. Afterwards, the actually found keyword is available in <code>\commalistelement</code>.
 
 
 
Sometimes an action needs to be undertaken that depends only on the first character of something (for instance, checking if some string represents a number or not). This macro get this character and puts it in <code>\firstcharacter</code>.
 
 
 
<texcode>
 
\getfirstcharacter {string}
 
</texcode>
 
 
 
== Comma separated lists ==
 
 
 
We've already seen some macros that take care of comma separated lists. Such list can be processed with
 
 
 
<texcode>
 
\processcommalist[string,string,...]\commando
 
</texcode>
 
 
 
The user supplied command <code>\commando</code> receives one argument: the string. This command  permits nesting and spaces after commas are skipped. Empty sets are no problem.
 
 
 
Empty arguments are not processed. Empty items (,,) however are treated.
 
 
 
<context source="yes" text="And here is the result:">
 
\def\dosomething#1{(#1)}
 
1: \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par
 
2: \processcommalist [{a,b,c,d,e,f}]        \dosomething \par
 
3: \processcommalist [{a,b,c},d,e,f]        \dosomething \par
 
4: \processcommalist [a,b,{c,d,e},f]        \dosomething \par
 
5: \processcommalist [a{b,c},d,e,f]        \dosomething \par
 
6: \processcommalist [{a,b}c,d,e,f]        \dosomething \par
 
7: \processcommalist []                    \dosomething \par
 
8: \processcommalist [{[}]                  \dosomething \par
 
</context>
 
 
 
Quitting a commalist halfway can be done by using <code>\quitcommalist</code> within the execution macro.
 
 
 
When a list is saved in a macro, we can use a construction like:
 
 
 
<texcode>
 
\expandafter\processcommalist\expandafter[\list]\dosomething
 
</texcode>
 
 
 
Such solutions suit most situations, but we wanted a bit more.
 
 
 
<texcode>
 
\processcommacommand[string,\stringset,string]\dosomething
 
</texcode>
 
 
 
where <code>\stringset</code> is a predefined set, like:
 
 
 
<texcode>
 
\def\first{zeroeth,first}
 
\def\second{last}
 
\processcommacommand[\first]\message
 
\processcommacommand[\first,second,third]\message
 
\processcommacommand[\first,between,\second]\message
 
</texcode>
 
 
 
Commands that are part of the list are expanded, so the use of this macro has its limititations.
 
 
 
The argument to <code>\dosomething</code> is not delimited. Because we often use <code>[]</code> as delimiters, we also have:
 
 
 
<texcode>
 
\processcommalistwithparameters[string,string,...]\dosomething
 
</texcode>
 
 
 
where <code>\dosomething</code> looks like:
 
 
 
<texcode>
 
\def\dosomething[#1]{... #1 ...}
 
</texcode>
 
 
 
Some of the commands mentioned earlier are effective but slow. When one is desperately in need of faster alternatives and when the conditions are predictable safe, the <code>\raw</code> alternatives come into focus. A major drawback is that they do not take <code>\c!constants</code> into account, simply because no expansion is done. (This is not a problem for <code>\rawprocesscommalist</code>), because this macro does not compare anything. Expandable macros are permitted as search string for <code>\rawdoifinsetelse</code>.
 
 
 
<texcode>
 
\rawdoifinsetelse{string}{string,...}{...}{...}
 
\rawprocesscommalist[string,string,...]\commando
 
\rawprocessaction[x][a=>\a,b=>\b,c=>\c]
 
</texcode>
 
 
 
Spaces embedded in the list, for instance after commas, spoil the search process. The gain in speed depends on the length of the argument (the longer the argument, the less gain).
 
 
 
When we process the list <code>a,b,c,d,e</code>, the raw routine takes over 30% less time, when we feed 20+ character strings we gain about 20%.
 
 
 
It's possible to get an element from a commalist or a command representing a commalist.
 
 
 
<texcode>
 
\getfromcommalist    [string] [n]
 
\getfromcommacommand [string,\strings,string,...] [n]
 
</texcode>
 
 
 
The difference between the two of them is the same as the difference between <code>\processcomma...</code>. The found string is stored in <code>\commalistelement</code>.
 
 
 
Because 0, 1 and 2 are often asked for, the macros are optimized on those numbers.
 
 
 
We can calculate the size of a comma separated list by using:
 
 
 
<texcode>
 
\getcommalistsize    [string,string,...]
 
\getcommacommandsize [string,\strings,string,...]
 
</texcode>
 
 
 
Afterwards, the length is available in the macro <code>\commalistsize</code> (not a <''counter''>).
 
 
 
Watertight (and efficient) solutions are hard to find, due to the handling of braces during parameters passing and scanning. Nevertheless, the macros function quite well.
 
 
 
For low level (fast) purposes, we can also use the next alternative, which can handle up to 8 elements at most.
 
 
 
<texcode>
 
\dogetcommalistelement1\from a,b,c\to\commalistelement
 
</texcode>
 
 
 
It's ugly, but it is very fast indeed. Keep in mind that this version does '''not''' strip leading spaces from the list items.
 
 
 
== Assignments and parameters ==
 
 
 
Assignments are the backbone of ConTeXt. Abhorred by the concept of style file hacking, we took a considerable effort in building a parameterized system. Unfortunately there is a price to pay in terms of speed. Compared to other packages and taking the functionality of ConTeXt into account, the total size of the format file is still very acceptable. Now how are these assignments done.
 
 
 
Assignments can be realized with:
 
 
 
<texcode>
 
\doassign[label][variable=value]
 
\undoassign[label][variable=value]
 
</texcode>
 
 
 
and:
 
 
 
<texcode>
 
\doassignempty[label][variable=value]
 
</texcode>
 
 
 
These macros are a syntactic rewrite for the 'set', 'clear' and 'initialize' actions:
 
 
 
<texcode>
 
\def\labelvariable{value}      % \doassign
 
\def\labelvariable{}          % \undoassign
 
\doifundefined{\labelvariable}
 
  {\def\labelvariable{value}} % \doassignempty
 
</texcode>
 
 
 
Using the assignment commands directly is not our ideal of user friendly interfacing, so we take some further steps.
 
 
 
<texcode>
 
\getparameters [label] [...=...,...=...]
 
</texcode>
 
 
 
Again, the label identifies the category a variable belongs to. The second argument can be a comma separated list of assignments. Duplicates are allowed, later appearances overrule earlier ones.
 
 
 
<texcode>
 
\getparameters
 
  [demo]
 
  [alfa=1,
 
  beta=2]
 
</texcode>
 
 
 
is equivalent to
 
 
 
<texcode>
 
\def\demoalfa{1}
 
\def\demobeta{2}
 
</texcode>
 
 
 
Some explanation of the inner workings of ConTeXt is in order here to make sure the source is understandable for readers, since the actual internal usage is a bit more complex than this.
 
 
 
In the pre-multi-lingual (simple) stadium ConTeXt took the next approach. With these definitions (that are mainly there to conserve TeX's string space):
 
 
 
<texcode>
 
\def\??demo {@@demo}
 
\def\!!width {width}
 
\def\!!height {height}
 
</texcode>
 
 
 
calling
 
 
 
<texcode>
 
\getparameters
 
  [\??demo]
 
  [\!!width=one,
 
  \!!height=2]
 
</texcode>
 
 
 
lead to:
 
 
 
<texcode>
 
\def\@@demowidth{one}
 
\def\@@demoheight{2}
 
</texcode>
 
 
 
Because we want to be able to distinguish the <code>!!</code> pre-tagged user supplied variables from internal counterparts, we will introduce a slightly different tag in the multi-lingual modules. There we will use <code>c!</code> or <code>v!</code>, depending on the context.
 
 
 
The call will typically somewhat look like this:
 
 
 
<texcode>
 
\getparameters
 
  [\??demo]
 
  [\c!width=\v!one,
 
  \c!height=2]
 
</texcode>
 
 
 
In the dutch interface, this would (e.g.) expand into:
 
 
 
<texcode>
 
\def\@@demobreedte{een}
 
\def\@@demohoogte{2}
 
</texcode>
 
 
 
but in the english interface it would become:
 
 
 
<texcode>
 
\def\@@demowidth{one}
 
\def\@@demoheight{2}
 
</texcode>
 
 
 
ConTeXt continues to function, because it never uses the explicit expansion, anywhere. It always relies on the <code>\s!</code> and <code>\v!</code> definitions (remember I told you not to touch them? this is why.)
 
 
 
Sometimes we explicitly want variables to default to an empty string, so we welcome:
 
 
 
<texcode>
 
\getemptyparameters [label] [...=...,...=...]
 
</texcode>
 
 
 
Some ConTeXt commands take their default setups from others. All commands that are  able to provide backgounds or rules around some content, for instance, default to the standard command for ruled boxes. In situations like this we can use:
 
 
 
<texcode>
 
\copyparameters [to-label] [from-label] [name1,name2,...]
 
</texcode>
 
 
 
For instance
 
 
 
<texcode>
 
\copyparameters
 
  [\??internal][\??external]
 
  [\c!width,\c!height]
 
</texcode>
 
 
 
Leads to (english version):
 
 
 
<texcode>
 
\def\@@internalwidth  {\@@externalwidth}
 
\def\@@internalheight {\@@externalheight}
 
</texcode>
 
 
 
A lot of ConTeXt commands take optional arguments, for instance:
 
 
 
<texcode>
 
\dothisorthat[alfa,beta]
 
\dothisorthat[first=foo,second=bar]
 
\dothisorthat[alfa,beta][first=foo,second=bar]
 
</texcode>
 
 
 
Although a combined solution is possible, we prefer a separation between argument keywords  and parameter assignments. The next command takes care of propper handling of such multi-faced commands.
 
 
 
<texcode>
 
\doifassignmentelse {...} {then ...} {else ...}
 
</texcode>
 
 
 
A slightly different approach is <code>\checkparameters</code>, which also checks on the presence of a <code>=</code>, just like the previous macro.
 
 
 
<texcode>
 
\checkparameters[argument]
 
</texcode>
 
 
 
The boolean <code>\ifparameters</code> can be used afterwards to verify that there were assignments in the supplied argument.
 
 
 
When working with delimited arguments, spaces and lineendings can interfere. The next set of macros uses TeX' internal scanner for grabbing everything between arguments. Forgive me the funny names.
 
 
 
<texcode>
 
\dosingleargument\command    = \command[#1]
 
\dodoubleargument\command    = \command[#1][#2]
 
\dotripleargument\command    = \command[#1][#2][#3]
 
\doquadrupleargument\command  = \command[#1][#2][#3][#4]
 
\doquintupleargument\command  = \command[#1][#2][#3][#4][#5]
 
\dosixtupleargument\command  = \command[#1][#2][#3][#4][#5][#6]
 
\doseventupleargument\command = \command[#1][#2][#3][#4][#5][#6][#7]
 
</texcode>
 
 
 
These macros are used in the following way:
 
 
 
<texcode>
 
\def\docommand[#1][#2]%
 
  {... #1 ... #2 ...}
 
 
 
\def\command%
 
  {\dodoubleargument\docommand}
 
</texcode>
 
 
 
So <code>\dodoubleargument</code> leads to:
 
 
 
<texcode>
 
\docommand[#1][#2]
 
\docommand[#1][]
 
\docommand[][]
 
</texcode>
 
 
 
The macros above insure that the resulting call always has the correct number of bracket pairs, even if the user did not supply all of the options. In this case, a number of trailing bracket pairs are empty. A set of related <code>\if</code> booleans is initialized to give you access to the number of user supplied parameters.
 
 
 
These maybe too mysterious macros enable us to handle more than one setup at once.
 
 
 
<texcode>
 
\dosingleargumentwithset \command[#1]
 
\dodoubleargumentwithset \command[#1][#2]
 
\dotripleargumentwithset \command[#1][#2][#3]
 
\dodoubleemptywithset    \command[#1][#2]
 
\dotripleemptywithset    \command[#1][#2][#3]
 
</texcode>
 
 
 
The first macro calls <code>\command[##1]</code> for each string in the set <code>#1</code>. The second one calls for <code>\commando[##1][#2]</code> and the third, well one may guess. These commands support constructions like:
 
 
 
<texcode>
 
\def\dodefinesomething[#1][#2]%
 
  {\getparameters[\??xx#1][#2]}
 
 
 
\def\definesomething%
 
  {\dodoubleargumentwithset\dodefinesomething}
 
</texcode>
 
 
 
Which accepts calls like:
 
 
 
<texcode>
 
\definesomething[alfa,beta,...][variable=...,...]
 
</texcode>
 
 
 
Now a whole bunch of variables like <code>\@@xxalfavariable</code> and <code>\@@xxbetavariable</code> is defined.
 
 
 
We've already seen some commands that take care of optional arguments between <code>[]</code>.
 
 
 
The next two commands handle the ones with <code>{}</code>. They are called as:
 
 
 
<texcode>
 
\dosinglegroupempty    \ineedONEargument
 
\dodoublegroupempty    \ineedTWOarguments
 
\dotriplegroupempty    \ineedTHREEarguments
 
\doquadruplegroupempty \ineedFOURarguments
 
\doquintuplegroupempty \ineedFIVEarguments
 
</texcode>
 
 
 
where <code>\ineedONEargument</code> takes one and the others two and three arguments, et cetera.
 
 
 
These macros were first needed in ppchTeX. At first glance, the functionality sounds trivial. But in fact, it is not. Consider this expanded input:
 
 
 
<texcode>
 
\def\test#1{\message{#1}}
 
\dosinglegroupempty\test {a}Text
 
\dosinglegroupempty\test Text
 
</texcode>
 
 
 
In the last line, <code>#1</code> will '''not''' print the letter <code>T</code>, as would be 'normal' TeX behaviour. These macros can explictly take care of spaces, which means that the next definition and calls are valid:
 
 
 
<texcode>
 
\dotriplegroupempty\test {a} {b} {c}
 
\dotriplegroupempty\test {a} {b}
 
\dotriplegroupempty\test
 
{a}
 
{b}
 
</texcode>
 
  
And alike.
+
== Contents ==
  
Just as their <code>[]</code>, they also set the <code>\ifXXXXargument</code> switches.
+
[[System Macros/Fundamentals|Fundamentals]]
  
== User interaction ==
+
[[System_Macros/Mnemonics_and_Aliases|Mnemonics & Aliases]]
  
This macro hardly needs explanation. It stops TeX until you input something.
+
[[System_Macros/Scratch_Variables|Scratch Variables]]
  
Macros for showing messages. In the multi-lingual modules, we will also introduce a mechanism for message passing. For the moment we stick to the core macros:
+
[[System_Macros/Expansion_Control|Expansion Control]]
  
<texcode>
+
[[System_Macros/Handling_Arguments|Handling Arguments]]
\writestring {string}
 
\writeline
 
\writestatus {category} {message}
 
</texcode>
 
  
Messages are formatted: the category appears within a fixed number of columns, on it's own, followed by a colon. One can provide the maximum width of the identification string with the macro <code>\statuswidth</code>.
+
[[System_Macros/Definitions_and_Assignments|Definitions & Assignments]]
  
For debugging purposes we can enhance macros with the next alternative. Here <code>debuggerinfo</code> stands for both a macro accepting two arguments and a boolean (in fact a few macro's too).
+
[[System_Macros/Branches_and_Decisions|Branches & Decisions]]
  
<texcode>
+
[[System_Macros/Loops_and_Recursion|Loops & Recursion]]
\debuggerinfo {subcategory} {message}
 
</texcode>
 
  
This message will only be output if <code>\debuggerinfo</code> is true. (it usually isn't). If you need to calculate something to be used inside the message, embrace your calculating code with <code>\ifdebuggerinfo ... \fi</code>.
+
[[System_Macros/Action_Processing|Action Processing]]
  
 +
[[System_Macros/Comma_Separated_Lists|Comma Separated Lists]]
  
 +
[[System_Macros/Key_Value_Assignments|Key-Value Assignments]]
  
 +
[[System_Macros/User_Interaction|User Interaction]]
  
[[Category:ConTeXt programming]]
+
[[Category:Programming and Databases]]
[[Category:Inside ConTeXt]]
+
[[Category:Tools]]

Latest revision as of 16:49, 8 June 2020

Introduction

All large macro packages for TeX have the need for a number of low-level macros to ease the programming effort. This is definitely true for the ConTeXt package, where the extensive use of key-value pairs and the multilingual interface introduce extra complications to the already tricky art of TeX programming.

Some of these internal macros are real gems, and nearly all of them can also be used in your own source documents. Although most of ConTeXt is described in the source code, sometimes the explanations are too technical for a casual user, and some of the documentation, especially the examples, is still in dutch.

The 'System Macros' documentation attempts to explain the most usable commands of the internal macro layer, using english examples to clarify usage.

Disclaimer: Quite a lot of the explanation text is copied from source code documentation by Hans Hagen. Always assume that the errors are mine and the good jokes are his.

The current text will mostly deal with the file syst-gen.mkii (see syst-aux.mkiv for MkIV). Most of those macros are very basic, and many of them are related to programming constructs like flow control (\if statements), data structures (comma separated lists), and definitions.

Contents

Fundamentals

Mnemonics & Aliases

Scratch Variables

Expansion Control

Handling Arguments

Definitions & Assignments

Branches & Decisions

Loops & Recursion

Action Processing

Comma Separated Lists

Key-Value Assignments

User Interaction