yodlprogram from the point of a meta-user, one who is interested in how macro files work, or one who wants to write a new converter. If you're just interested in using Yodl with the pre-existing converters and macro files, skip this chapter and continue with the macro package description (chapter 4).
Yodl program the main converter of the Yodl package. The basic usage
yodl's built-in macros, and the syntax of the
Yodl language is described here.
Yodlreads one or more input files, interprets the commands therein, and writes one output file. The program is started as:
-x, --optionnameare two alternate ways to specify option x. If
-xtakes an argument, it may be specified immediately following the
-x, but separating blanks may also be used. Options not taking arguments can be combined (e.g.,
-a -b -cmay be combined to
-abc). Arguments specified with long options should be separated from the long option using a
The following options are currently available:
-D, --define=NAME[=VALUE]: Defines name as a symbol. This option is acts like
NAMEis initialized to
-d, --definemacro=NAME=EXPANSION: Defines
NAMEas macro expanding to
-h, --help: usage information is written to the standard error stream, describing all of Yodl's options.
-i, --index[=file]: `file' is the name of the index file. By default
<outputbase>.idxis used. No default when output is written to stdout. The index file is processed by Yodl's post-processor,
-I, --include=DIR: This defines the system-wide include directory where
Yodlsearches for its input files. E.g. a statement to include a given file, like:
INCLUDEFILE(latex)Yodl now searches for the file
latexin the current directory, and when that fails, in the system-wide include directory. The system-wide include directory is typically the place where the maintainer of a system stores macro-files for Yodl. This searching process applies to files that are included inside a document but also applies to filenames on the command line when invoking the
The name of the included file (
latex in the above example) is the bare
Yodl supplies a default extension (
.yo), if necessary.
-I option overrules Yodl's built-in name for the system-wide
include directory. The built-in name is defined when compiling Yodl, and is,
/usr/share/yodl. Furthermore, the definition may contain $HOME,
which is replaced by the user's home directory if the `home' or `HOME'
environment variable is defined. It may also contain $STD_INCLUDE, which
is replaced by the compilation defined standard include path. The standard
includepath may be overruled by either (in that order) the command line switch
-I or the
tt(Yodl)_INCLUDE_PATH environment variable. By default, the
current directory is added to the standard include path. Hewver, when
tt(Yodl)_INCLUDE_PATH is used, the current directory must be mentioned
explicitly. The individual directories need not be terminated by a
/-character. In distributed
.deb archives, the standard directory is
/usr/share/yodl (prefixed by the current working directory).
-k, --keep-ws: Since
Yodlversion 2.00 blanks at the begin and end of lines are ignored, even without a trailing \, when the `white space level' is non-zero. Earlier versions kept these blanks. The legacy handling of white space at end of lines can by obtained using the
-kflag. Note that white space are always kept when using verbatim copying, and when the white-space level is zero.
-m, --messages=SET: Set the so-called `message level' to a combination of the SET
acdeinw. The letters of this set have the following meanings:
a: alert. When an alert-error occurs, Yodl terminates. Here Yodl requests something of the system (like a
get_cwd()), but the system fails.
c: critical. When a critical error occurs, Yodl terminates. The message itself can be suppressed, but exiting can't. A critical condition is, e.g., the omission of an open parenthesis at a location where a parenthesized argument should appear, or a non-existing file in an
INCLUDEFILEspecification (as this file should be parsed). A non-existing file with a
NOEXPANDINCLUDEspecification is a plain (non-critical) error.
d: debug. Probably too much info, like getting information about each character that was read by Yodl.
e: error. An error (like doubly defined symbols). Once an error has been encountered the remainder of the input is still parsed (up to a maximum number of errors), but no output is generated.
i: info. Not as detailed as `debug', but still very much info, like information about media switches.
n: notice. Information about, e.g., calls to the builtin function calls.
w: warning. Something you should know about, but probably not affecting Yodl's proper functioning
-n, --max-nested-files=NR: This option causes Yodl to abort when the number of nested input files exceeds
NR, which is 20 by default. Exceeding this number usually means a circular definition somewhere in the document. This is the case when, a file
a.yoetc.. It does not prevent recursive macro- or subst-replacements. For that the
--max-replacements) option is available.
-o, --output=FILE: This option causes Yodl to write its output to
FILE. By default, the output goes to the standard output stream. E.g., you can use
Yodlto read a file
inputand to write to
outputwith the following two commands:
yodl input > output yodl -ooutput inputThe difference being that in the latter case an index file is generated, but not in the former case. Notice that writing an index file can be forced when the
--indexoption is specified.
-p, --preload=CMD: This option `pre-loads' the string
cmd. It acts as though
cmdwas the first command in the first input file that is processed by
More than one
--preload=CMD options may be present on the command
line. Each of the commands is then processed in turn, before reading
-r, --max-replacements=NR: This option causes Yodl to abort when the number of macro calls or subst-replacements exceeds
NR * 10,000. By default,
NRequals 1. Setting
--max-replacements=0implies that no macro- or subst-replacement checks are performed.
-t, --trace: This option enables tracing: while parsing, Yodl writes its output to the standard error stream. As is the case with the
-koption, this option is defined for debugging purposes only.
-V, --version. This option shows
Yodl's actual version.
-v, --verbose: This option increases Yodl's `verbosity level' and may occur more than once. By default yodl shows alerting, critical, emergency and error messages. Each
--verboseoption adds a next message level. In order, warning, notice, info and debug messages are added to this set. It is also possible to suppress messages. The
VERBOSITYbuiltin can be used for that.
-W, --warranty. This option shows a warranty disclaimer and a copyright notice.
-w, --warn: The presence of this option caused Yodl to warn when, e.g., symbols are redefined.
The inputfile elements on the command line specify which files Yodl should
process. All names are supplied with an extension (this extension is
defined in the installation of Yodl and is usually
.yo). The files are then
searched for in the directories mentioned in the include-path. Files may also
be specified using absolute pathnames.
Note that all filenames on the command line are input files. To define an
output file, either use the
--output option or redirect the output.
Yodlmixes `real' text that should appear on the output with markups: commands for
Yodl. The markups must follow a certain grammar, which is described in this section.
Yodltherefore falls in the category of `markup languages', in contrast to `WYSIWYG'-programs. As a consequence,
Yodlpromotes concept-oriented writing.
Basically, Yodl only does `something special' when it encounters the name of a builtin function or the name of a user-defined macro, followed by a parenthesized argument. Sometimes a function or macro requires multiple arguments, which must then be specified in sequence. All required parameter lists, however, must be specified within the same input file. It is not allowed to split the activation of a builtin function or macro over multiple input files. Plain text, on the other hand, may be split over multiple files.
In this section the elements of
Yodl's grammar are briefly discussed.
Yodl's lexical scanner returns small pieces of information to its parser. These pieces of information are called tokens, and consist of elements like a blank space, a non-blank character, or an end-of-ile flag. These tokens are at too small an aggregation level to be useful for the current user-guide, so here we concentrate our discussion on the next aggregation level: compound elements and conceptual elements.
Compound elements relate to the basic tokens as words in a sentence to the individual letters of the words. These compound elements are identifiers, names, numbers, parameter lists and arguments.
Conceptual elements are found at the next higher aggregation levels:
builtin functions are the buildin blocks for all of
symbols and counters are
Yodl's variables, and (user defined)
Yodl's functionality beyond those of the basic builtin
In the coming sections these basic and conceptual elements are discussed in greater detail.
Yodllanguage. E.g., the sequence
INCLUDEFILEis an identifier: when followed by a filename in parentheses,
Yodlwill take some special action (in this case, read the file as a
Identifiers may consist of uppercase or lowercase characters. No other characters may appear in them.
In particular, note that this diverts from the well known definition for
identifiers used in most programming languages: identifiers may not contain
underscores, nor digits.
Yodl, therefore, won't accept identifiers like
Names are sequences of characters, not containing white space characters.
(i.e., any series of characters not containing spaces, tabs or
newlines). Names are allowed with certain builtin functions, liek the
INCLUDEFILE function, expecting the name of a file as its argument.
A-F), representing the decimal values 10 until 15, respectively. Hexadecimal values are usually prefixed by
0x, for example
In other contexts (in particular, with character tables 2.3), octal numbers or character constants are allowed too.
An octal number only consists of the digits 0-7. In
Yodl, octal values
must consist of three digits, and must be preceded by a backslash.
Character constants may very well be considered numerical values. Character
constants consist of a character value between single quotes, for example
Refer to section 2.3 for more detailed information about the use of octal values and character constants.
Yodl has no concept of floating point values nor does it have facilities for
performing floating point arithmetic.
Yodlbuiltin functions or user-defined macros must be surrounded by a pair of matching open- and close-parentheses. Each parameter list contains exactly one argument.
An argument is recognized when it immediately follows the name of a builtin function or user-defined macro. Some functions or macros expect multiple arguments. In those cases, the required number of arguments must be provided, possibly separated from each other by white-space.
For example, the following shows how to call the builtin function
DEFINECOUNTER, expecting two arguments:
DEFINECOUNTER(MyCounter)() DEFINECOUNTER(MyCounter) () DEFINECOUNTER(MyCounter)(12)
Yodl recognizes the arguments of macros when delimited by
) characters. As long as the numbers of opening and closing
Yodl correctly recognizes the list. E.g., given a
somemacro, the following code sample shows the macro
followed by one argument:
somemacro(Here is a chunk of text.) somemacro(Here is a some (more) text.)An error occurs when the parentheses aren't balanced: i.e., when the argument consists of more opening than closing parentheses or vice versa To handle such situations, Yodl offers a `literal-character' mechanism (see the
CHARmacro in 3.1.4) and a `global substitution' mechanism (see the
SUBSTmacro in 3.1.62). For example, to send the text
here's a ")" closing parenthesisas an argument to our hypothetical macro
somemacro, the following can be used:
COMMENT(-- Alternative 1: using CHAR --) somemacro(here's a "CHAR(41)" closing parenthesis) COMMENT(-- Alternative 2: using SUBST --) SUBST(closepar)(CHAR(41)) somemacro(here's a "closepar" closing parenthesis)Both methods have disadvantages: the
CHARmethod requires you to remember that an ASCII 41 is a closing parenthesis. The
SUBSTmethod defines a string
closeparthat is always expanded to a closing parenthesis, wherever it may occur in the text. But whatever method is used, it should be clear by now that unbalanced arguments can be handled by Yodl. Also, remember that unbalanced parenthesis pairs are only relevant in argument lists. Yodl handles parentheses in normal text as ordinary characters.
Yodl's functionality are its builtin functions. Builtin functions exists to manipulate all of
Yodl's builtin types (character tables, counters, macros and symbols) and to do basic bookkeeping and flow-control: it is possible to test values of counters and symbols, to include other input files, to generate warning and error messages, and to start child- or subprocesses. Each builtin function is described in a separate subsection of section BUILTIN 3.1.
html, the \
'eis written as
é, but in LaTeX it's written as \
'e. Rather than using a potentially long if-else ladder to determine how to set a particular character, a character translation table can be used. The character translation table of a particular conversion is then activated only for that type of conversion.
Character table translations are used very late during the processing of
Yodl's input s: it is the output generator that handles the character
translations. Consequently, macros or builtin function calls that might appear
in a character's redefinition element of a character table are not
expanded. In practice this never is a point of concern. In section
2.3 the use of character translation tables is discussed in
Therefore, a macro package that converts a Yodl document to LaTeX doesn't need
to provide the numbering of sections etc.. However, if you do want the
numbering and if you want to convert documents to, say,
html, then you
must take care of the numbering yourself.
Counters exist to make this possible. Counters can be incremented, can be given a particular value, can be given a new value temporarily and can be removed. They always contain integral values, which may be negative.
Section 2.5 describes the use of counters in more detail.
Yodlinput files. Macros add functionality to
Yodlexceeding the basic functionality of the builtin functions. Macros can have arguments, and they are used in exactly the same way as builtin functions are used.
Yodl encounters a macro, it acts as follows:
#is the sequence number of a particular argument) are replaced by the literal text of the corresponding macro's arguments.
Yodl's lexical scanner.
Defining macros is described in section 3.1.11. Macros may be defined, deleted, renamed, and temporarily given other definitions.
Yodlis started using the
-wflag on the command line, then warnings are generated when Yodl encounters a possible macro name, followed by a parenthesized argument, without finding a macro by that name. Yodl then prints something like
cannot expand possible user macro.
Examples of such sequences are,
The necessary file(s) are here, or
the manual page for sed(1). The candidate macros are
these names could very well have been `valid' user macros followed by their
A nousermacro can be defined to suppress these warnings, by informing
sed aren't macros. Nousermacros may be defined and
undefined. See sections 3.1.41 and 3.1.16 for
Yodlsymbols contain text. They were introduced to allow the flexible expansion of text, the length and/or content of which cannot be determined in advance. In particular, symbols are useful to store a series of LaTeX document options, or a series of
htmlbody options. In earlier versions of
Yodlcomplex and confusing constructions using nested definitions of macros were used for this. These macros were not only confusingly complex, but they also suffered from a hard-coded maximum. Symbols solve these drawbacks, and now that they are available, they are used for all natural situations in which an initially unknown piece of text must be stored. National language specific strings are another useful area in which symbols can be used. The symbol
CONTENTSHEADINGcan be set to the name of the contents heading (e.g.,
Contenidoin Spanish, and macros can simply insert the value of the symbol
CONTENTSHEADINGat the appropriate location.
Symbols can be defined 3.1.12, removed 3.1.17,
(temporarily 3.1.57 or permanently 3.1.61) be given
another value; pushed symbol values can be restored 3.1.51 at a
later point. Of course, their values can also be inserted 3.1.63
Yodl's output file.
Yodlallows you to end a line with a backslash character \ and to continue it on the next line. That way you can split long lines to fit your screen. When processing its input,
Yodltreats these lines as one long line, and ignores the final \ character. This feature only works when the \ character is the last one on the line (only a physical newline may follow).
When the line following the one with the \ character has leading spaces,
then these are omitted. This allows you to `indent' a file as you wish, while
the space characters of the indentation are ignored by the
A trivial example is the following:
Grandpa and\ grandma are sitting on the sofa.Due to the occurrence of the \ character in the sequence
Yodlcombines the lines to
Grandpa andgrandma are sitting on the sofa.Note that the spaces before
grandmaare ignored, since this is the second line following a \ character.
If you do want one or more spaces while joining lines with \, put the
spaces before the \ character.
Yodlignores leading spaces of the second line.
Yodlto merge it with the following line? In such a case, type a space character following your \:
Yodlwon't combine the lines. Or set the \ character as
CHAR(92)(see section 3.1.4 for the
Yodl processes input files, and the white-space level exceeds zero
(see section 3.1.35), then all lines are processed as if they
terminated by a \. This behavior was implemented first with
2.00. It can be suppressed using
footnotefor you (someone did, in fact, see the next chapter), to typeset footnotes. If you'd type in a document:
The C Programming Languagefootnote(as defined by Kernighan and Ritchie) ...
then of course Yodl would fail to see the start of a macro in the sequence
Languagefootnote. You could say
The C Programming Language footnote(as defined by Kernighan and Ritchie) ...
but that would introduce a space between
Language and the footnote.
Probably you don't want that, since spaces between a word and a footnote
number look awful and because of the fact that the footnote number might be
typeset on the following line.
For these special situations, Yodl recognizes the
+identifier sequence as
the start of a macro, while the
+ sign is effectively ignored. In the
above example you could therefore use
The C Programming Language+footnote(as defined by Kernighan and Ritchie) ...
+identifier recognition only works when the identifier following the
+ sign is a macro. In all other situations, a
+ is just a plus-sign.
+identifier sequence furthermore plays an important role in macro
packages. If you're interested, see the file
shared.yo which is by default
NOUSERMACRObuiltin command (cf. section 3.1.41) may be used to suppress the interpretation of a character sequence (e.g.,
file(...)) as a macro, but what if a macro should not be expanded in the occasional situation? For this case various solutions are available:
verb(...)macros may be used to suppress macro expansion. These macros also temporarily alter the typesetting font, though.
NOEXPAND()builtin command may be used: the macro name may be passed to
NOEXPAND(), immediately followed by the `argument list':
Like this: NOEXPAND(NOEXPAND)(hello world)
nop()macro may be used to separate a macro name from its argument list:
Like this: NOEXPAND+nop()(hello world)
There are two main reasons for the need of character translation tables.
First, a document language becomes much easier to use when you can type an
asterisk as * instead of
\verb/*/ (these are sequences from the
LaTeX document language). Hence, a mechanism that expands a * in the input to
\verb/*/ on the output, saves the users a lot of typing.
Second, forcing users to type weird sequences won't work if you're planning on
converting the same Yodl document to a different output format. If the user
\verb/*/ in the input to typeset an asterisk in the output, how
should he or she arrive at a single * in the output in another output format?
The solution is of course to define the translation for an input character like * given the output format.
DEFINECHARTABLEdefines a character translation table. It takes two arguments: the name of the table and the character translations. Hence, each table is defined by its own name.
As an example of a table, consider the following fragment. It defines a table
that translates the upper case characters
E to their lower case
DEFINECHARTABLE(tolower)( 'A' = "a" 'B' = "b" 'C' = "c" 'D' = "d" 'E' = "e" )
DEFINECHARTABLE statement must have a non-empty second
parameter. "Empty" character tables cannot be defined, though one
non-translation table is built-in.
The syntaxis of the second argument is as follows:
cbeing any character. Escape-sequences from the C programming language can be used in this specification; Yodl supports the sequences
\v(vertical tab). Octal and hexadecimal constants may also be used. E.g., character
Ymay also be specified using the octal value
\131or the hexadecimal value
\x59. Any other character following a \ defines itself:
\\represents a single backslash character.
'\n' = "End of line\n"
Such a mapping adds the text
End of line to each line, since each
newline character in the input is replaced by the text
End of line,
followed by the newline itself.
Starting with Yodl 2.14.0 octal and hexadecimal constants may also be used
within the double quoted string. E.g., character
Y may also be
specified using the octal value
\131 or the hexadecimal value
\x59. As an example where the octal/hexadecimal values may be useful
consider the processing of a man-page. The character representations for
the literal double quote (
\(dq\&. However, since
( cannot be written literally
in the character translation table since that would result in unbalanced
parentheses while processing the character table's definition. Also,
(40) cannot be used, since character table conversiond are
performed by the output generator, which is called after the macro
expansions have been performed. This it would result in the literal text
CHAR((40)) appearing in the manual page.
Using the octal character representation in the chartable specification
" character appearing in man-page the problem can now be
solved. The actual specification used is:
'"' = "\\\050dq\\&"
Translations which are not specified in the table are left to the default, which is to output the character as-is.
Note that the character table translation is something that the
program does as one of its last actions, just before sending text to the
output file. The expansion text is not further processed by
for the conversion of C-type escape sequences to ordinary characters. The
expansion text should therefore not be protected by, e.g.,
(unless of course you want some character to generate the text
on the output).
USECHARTABLE. This macro takes one argument, which may be:
The default mapping, selected when an empty argument is given, means that Yodl enters its `zero translation state', meaning no character translation at all.
USECHARTABLE(), Yodl has one other mechanism of activating and deactivating character translation tables. This mechanism uses a stack, and hence, the related macros are appropriately named
PUSHCHARTABLE(name)pushes the currently active translation table onto a stack, and activates the table identified by
name. The argument may be emtpy; in that case, the zero-translation table is activated (analogously to
POPCHARTABLE()activates the translation table that was last pushed. There is no argument to this macro.
Using the push/pop mechanism is handy when a table must be temporarily
activated, but when it is not known which table exacty is active prior to the
temporary activation. E.g., imagine that you need to use a character table
listing to typeset a listing, but that you do not know the current
table. The pushing and popping mechanism is then used as follows:
COMMENT(First, we save the current table on the stack and we activate our "listing" table.) PUSHCHARTABLE(listing) COMMENT(Now the text is question is typeset.) ... COMMENT(The previously active table is re-activated, whatever its name.) POPCHARTABLE()
CHARtakes one argument: the ASCII number of a character or the character itself. The character is sent to the output file without being translated with the currently active character translation table.
NOTRANStakes one argument: the text in question. The text is neither parsed (i.e., macros in it are not expanded), nor translated with the current character translation table.
NOTRANSmacro is conceptually like a series of
NOEXPANDtakes one argument: the text in question. The text is not parsed, but it is translated with the current character translation table.
POPSUBSTtakes no argument, and restores the
SUBSTinterpretation status that was active just before the most recently called
PUSHSUBSTbuiltin macro call.
PUSHSUBSTtakes one argument: 0 or a non-zero value (commonly 1). Following
PUSHSUBSTS(0) SUBST-definitions are ignored; following
PUSHSUBSTS(1) SUBST-definitions are used.
NOEXPAND, consider the following. The
HTMLconverter (described in chapter 4) must be able to send HTML commands to the output file, but must also be able to send literal text (e.g., a source file listing). The HTML commands of course must be neither translated with any character table, nor must they be expanded in regard to macros. In contrast, a source file listing must be subject to character translations: the
>characters can cause difficulties. Two possible macros for a HTML converter are:
COMMENT(--- htmlcommand(cmd) sends its argument as a HTML command to the output ---) DEFINEMACRO(htmlcommand)(1)(NOTRANS(ARG1)) COMMENT(--- verb(listing) sends the listing to the output ---) DEFINECHARTABLE(list)( '&' = "&" '<' = "<" '>' = ">" ) DEFINEMACRO(verb)(1)( USECHARTABLE(list) NOTRANS(<listing>) NOEXPAND(ARG1) NOTRANS(</listing>) USECHARTABLE(standard) )In this example it is assumed that a character translation table
standardexists, defining the `normal' translations. This table is re-activated in the
The currently defined
verb macro for, e.g., html conversion looks like
DEFINEMACRO(verb)(1)( PUSHSUBST(0) NOTRANS(<pre>) XXnl() NOEXPAND(ARG1) XXnl() NOTRANS(</pre>) XXnl() POPSUBST() )Here, the surrounding
POPSUBST()pair prevent the interpretation of multi-char
verbenvironments, causing unexpected translations in pieces of code like
cout << "\"evil\" code\n";which, if the
PUSHSUBSTcommand isn't used, is converted to
cout << "évil\" code\n";
Therefore, a macro package that converts a Yodl document to LaTeX doesn't need to provide the numbering of sections etc.. However, if you do want the numbering and if you want to convert documents to, say, HTML, then you must take care of the numbering yourself.
This section describes the counters in Yodl: how to create counters, how to use them, etc..
PUSHCOUNTER. These functions expects two arguments: the name of the counter and an (optional) integral additive expression. When provided, the expression may contain binary + and unary or binary - operators; its operands may be integral values or the names of exising counters. Additive expressions may not contain white space characters. If an additive expression is provided it is evaluated and its value is then used to initialize the named counter. If the second argument is empty then the counter is initialized to zero (0).
For example, let's say that our macro package should provide two
subsection. The sections should be
numbered 0, 1, 2, etc., and the subsections 1.1, 1.2, 1.3 etc.. For this we
can define two counters:
DEFINECOUNTER(sectcounter)()\// initialized to 0 DEFINECOUNTER(subsectcounter)(1)\// initialized to 1
COUNTERVALUE(somecounter)expands to the value of
somecounter. E.g., if the current value is 2, then the value 2 is inserted into the output object. It is an error to use
COUNTERVALUEon a non-existing counter or on a counter not having a defined value (see below).
Yodl has several functions to modify and/or to set the values of counters. The
counter's value, named
expression below, is an additive expression: it may
contain binary + and unary or binary - operators. The operands may be integral
values of the names of exising counters. Additive expressions may not
contain blank spaces.
The functions modifying values of counters are:
POPCOUNTER(somecounter): This function pops the most recently pushed value off the counter's stack, assigning it to
somecounter. An error occurs when
somecounterdoesn't exist. If the counter was never pushed, it still exists following
POPCOUNTER, but its value is undefined: using
COUNTERVALUE(somecounter)in that case generates an error.
PUSHCOUNTER(somecounter)(expression): This function pushes the current value of the counter
somecounteron the counter's stack, making
expression'svalue its new value. Its second argument may be empty in which case the counter is initialized to 0. When
somecounterdoesn't yet exist, it is created with an initial value of
SETCOUNTER(somecounter)(expression): This function sets the value of
expression'svalue. The function does not expand to anything; i.e., it does not write to the output file.
ADDTOCOUNTER(somecounter)(expression): This function adds
USECOUNTER(somecounter): This function first increments
somecounter'svalue, and then writes the value of the counter to the output file.
This function is particularly useful in combination with
DEFINECOUNTER initializes a counter to zero,
USECOUNTER can be used to increment the value and to output it. The first
USECOUNTER is used on a new counter, the value 1 appears on
the output file. The next time, expression 2 appears on the output file etc..
Given the numbering requirements of the hypothetical commands
subsection (see the previous section), we can now complete the
DEFINECOUNTER(sectcounter) DEFINECOUNTER(subsectcounter) DEFINEMACRO(section)(1)(\ SETCOUNTER(subsectcounter)(0)\ USECOUNTER(sectcounter) ARG1) DEFINEMACRO(subsection)(1)(\ COUNTERVALUE(sectcounter).USECOUNTER(subsectcounter) ARG1)