CML Reference Guide

Chapter 3.1:  Macros

[TOP] [UP] [PREV] [NEXT]

CML provides a "macro" capability that may be used by both the CML code developer or the end user alike.  It is similar to the macro capability in the C language, or the functions that can be created in the Korn and other Unix shells.

A macro is just a shortcut, a way of representing a longer string of HTML and/or CML functions, with arguments that are expanded when the macro is evaluated. 

Macros are defined by the CML function $mac_define(); they are automatically evaluated (or "expanded") by the CML interpreter.  They may also be expanded with the $mac_expand() function, which is particularly useful when the macro reference is contained in a CML variable, or returned by a CML function. 

 
3.1.1 HTML Example
Here's a simple example of defining and using a macro that just acts as an HTML shortcut:

   eval $mac_define (image <IMG SRC="@1" WIDTH=@2 HEIGHT=@3>)

   %image (myimage.gif 64 32)

The invocation of the "%image", above, expands to:

   <IMG SRC="myimage.gif" WIDTH=64 HEIGHT=32>

Note that the "@1" in the macro definition represents the first word (or argument) in the macro invocation, "@2" the second word, and so on.  Macros support nine arguments, "@1" through "@9", plus the special argument "@@", which expands to the complete list of words in the macro invocation, no matter how many there are.

 
3.1.2 CML Example
Here's a more interesting macro definition.  (It assumes some knowledge of the CML functions $if(), $equal(), and $per_name().)

The "namer" macro displays the full name of a person, given their userid as the single argument -- for example, when invoked as %namer(roth).  But if that userid is this person (i.e., the current user), then the name is shown as blinking text.

   eval $mac_define (namer \$if (\$equal ($userid() @1) <BLINK>)\
                                 \$per_name(@1)</BLINK>)

Note the use of the backslash ("\") to "escape" some of the CML functions.  The backslash is removed during the definition of the macro, but it prevents the functions from being evaluated during the macro definition.  Instead, those functions are evaluated only when the macro is actually expanded.

For example, the $userid() function has no backslash, and thus is evaluated immediately, and becomes a constant part of the macro definition.  This is fine, since the userid is constant during the entire Caucus session.  $per_name(), on the other hand, is escaped with a backslash.  It must be evaluated during the macro expansion -- otherwise the definition would be attempting to find the name of the person called "@1"!

 
3.1.3 Macro expansion and CML evaluation
As stated in the previous section, when macros are expanded, any CML functions in the definition are automatically evaluated. 

The reverse, however, is not true.  If a CML function evaluates to a macro invocation, that macro is not automatically expanded.  (Only macro invocations in the original CML source code are automatically expanded.)

To expand a macro that results from a CML function evaluation, use the $mac_expand() function. 

For example, here is an actual line of CML code from the page that displays the responses to an item:

   "$cleanhtml (prohibited $protect($mac_expand($wrap2html($re_text()))))

Now suppose that the particular response from $re_text() is "word-wrapped" text that contains a macro invocation, such as the "%image" macro from 3.1.1.  Consider how that text is processed by the functions in this line.  $wrap2html() just handles word-wrapping and quoting of special HTML characters; it doesn't affect the macro.

Then $mac_expand() actually expands the macro, which can contain any CML functions, and evaluates them.  (Note that it only evaluates CML functions that are within the expansion of the macro.  Any other instances of CML functions in the argument text of $mac_expand() are left unevaluated.)

While this is very powerful, it can also be dangerous to allow any user to evaluate any CML function that might be in a macro.  So $protect() disables the effect of any CML functions that are considered "unsafe".  And finally, $cleanhtml() removes or hides any HTML tags that the Caucus manager or conference organizer has chosen to be prohibited, even if they were in the original macro definition.

 
3.1.4 Nested Macros
Macros may use other macros in their definitions.  Here is a very simple example that just uses HTML:

   "eval $mac_define (tag B)
   "eval $mac_define (emphasize <%tag()>@@</%tag()>)

   "Some words deserve %emphasize(special) emphasis!

And it produces the text "Some words deserve special emphasis!".  But, if instead we use backslashes to "escape" the invocations of %tag in the definition of %emphasize:

   "eval $mac_define (emphasize <\%tag()>@@</\%tag()>)

Now the expansion of %tag is delayed until the expansion of %emphasize.  This means that if we redefine %tag later on, %emphasize will use the new definition of %tag.

Macros may also use other macros in their expansion, in the same way that CML functions may call other CML functions as their arguments.  Here is another simple HTML-only example:

   "eval $mac_define (bold   <B>@@</B>)
   "eval $mac_define (italic <I>@@</I>)

   "Some words get %bold(%italic(double)) emphasis!

Which produces "Some words get double emphasis!".