Next: macroread() Up: MacAnova Help File Previous: macro_files   Contents

macro_syntax

Keywords: macros, syntax, control
This topic presumes familiarity with topic 'macros'.  It describes
the use of comments in macros, how values are returned, the special
symbols that may be used in macros and gives some tips on writing
robust and efficient macros.

Topics include Macro Self-Documentation, The Value of a Macro,
Referencing Macro Arguments, Use of Temporary Variables, Expansion of
Other Special Symbols Containing '$', Recursive Use of Macros, Tips For
Good Macro Writing, and Functions Useful in Macros

                        Macro Self-Documentation
Often the first few lines of a macro are comments ("#" at the start of
the line) which describe how the macro is used.  You can use command
macrousage() to print such comment lines.  When you write a macro, it is
good practice to include such lines.

                          The Value of a Macro
The last expression appearing in a macro is returned as its value.  If
this expression is the name of an "invisible" variable (name starting
with '_' or '@_') the macro value can be assigned or used in an
expression but will not be printed.  If no value is to be returned, the
macro should end with ';;' which ensures the value returned is NULL.

In addition, you can use 'return(Value)' almost anywhere in a macro to
immediately leave the macro, returning Value as the value of the macro.
Value may be a constant, expression or variable.  'return()' and
'return' without parentheses are equivalent to 'return(NULL)'.  See
topic 'return'.

See below for the use of delete() with keyword phrase return:T to return
a temporary variable as value.

                      Referencing Macro Arguments
In the text of a macro the place holders '$1', '$2', ... are used to
refer to argument 1, argument 2, ... .  When the macro is executed,
MacAnova examines its text and substitutes the appropriate argument,
almost exactly as typed, for any such place holder.  It then executes
the commands as if they had been typed at the keyboard.  For example, if
the first argument to a macro is '3+4', '@a <- $1' gets expanded to '@a
<- 3+4'.

When a macro references a missing argument (for example, $3 when there
are only two arguments) it usually terminates immediately with an error
message.  When, however, a missing argument appears in a quoted string
('print("$3")' when there are only two arguments) it is "expanded" to
nothing ('print("")').  When you use place holders of the form '$01',
'$02', ..., with a leading 0 for the number, then a missing argument is
expanded to NULL outside of a quoted string (see topic 'NULL').  This
may or may not result in an error message if the argument is used, but
you can test the value using one or more of the isxxxx() functions (see
below).

When an argument which is itself a quoted string, possibly containing
backslashes ('\'), is referenced in the macro as part of a quoted
string, then all instances of '"' and '\' in the argument are changed to
'\"' and '\\'.  For example, in a macro, 'print("$1")' expands to
'print("3+4")' when the first argument is '3+4' but expands to
'print("\"foo\"")' when the first argument is '"foo"'.

                       Use of Temporary Variables
It is good practice to use temporary variables (names starting with '@')
in macros so they will be deleted automatically just before the next
prompt.  You can also append '$$' to names to create a name that should
be unique to the macro (see below).  When you use 'dollars:T' as an
additional argument to macro() in creating a macro, '$$' will be
automatically appended to all temporary names not already ending in
'$$'.  This also happens when a macro with keyword DOLLARS on its header
line is read from a file.

Because temporary variables are not deleted until the next prompt, it is
often a good idea to delete them before exiting a macro, except for a
result being returned, of course.  To delete a variable, say '@result',
whose value is to be returned, the last command in the macro should be
delete(@result,return:T).  @result will be deleted but its value will
still be returned.  See delete().

Occasionally you may want to create a non-temporary "invisible"
variable, that is, one that is not normally listed by list() or
listbrief().  You do this by assigning a name starting with '_' such as
'_x'.  One use for this is to create a variable to be used by several
related macros but not directly referenced by the user.  See also list()
and listbrief().  A temporary variable is "invisible" if its name starts
with '@_', say '@_x'.  See topic 'variables:"invisible"'.

You can return an "invisible" result by

  delete(@result,return:T,invisible:T)

           Expansion of Other Special Symbols Containing '$'
In a macro, certain symbols starting with '$' have a special meaning.
Although they would be errors outside a macro, in a macro these symbols
are "expanded", that is, something is substituted for them in the text
of the macro.  In brief, these symbols are as follows:

  Symbol     Expanded value
  $0         The complete comma-separated list of macro arguments.
  $N         The number of macro arguments = 1 + (number of separating
             commas) except when the agument list is empty, when the
             value is 0
  $V         A comma separated list of all macro arguments that are not
             keyword phrases.
  $v         The number of macro arguments that are not keyword phrases.
  $K         A comma separated list of all macro arguments that are
             keyword phrases.
  $k         The number of macro arguments that are keyword phrases.
  $A         A CHARACTER vector whose elements are the character strings
             specifying the arguments, that is, vector("$1","$2",...).
  $S         The name of the macro.
  $$         Expands to a unique 2 digit number, 00, 01, ..., 49 in out-
             of-line macros and 50, 51, ..., 99 in in-line macros.

$N, $V, $v, $K, $k, $S and $A are not treated specially when they are
preceded or followed by a character that could be part of a variable
name.  For example, str$N would be interpreted as component N of
structure str (see 'structures') and $Na would not be changed and would
probably result in an error.

Here are fuller explanations or examples of the use of these special
symbols.  In all the following we assume the symbols are in a macro
invoked by mymacro(x, title:"Hello Dolly",run(5),new:T).

$0 (full list of arguments):
  In the example $0 is replaced by 'x,title:"Hello Dolly",run(5),new:T'.
  $0 is particularly useful in defining "aliases" of standard commands.
  For example,
   Cmd> dir <- macro("list($0)")
  defines macro dir() which is used identically to list().

  When $0 appears in a quoted string, all instances of '"' or '\' in any
  macro argument are prefixed by '\'.  In the example, "$0" is replaced
  by "x, title:\"Hello Dolly\",run(5),new:T".

$N (number of arguments):
  In the example, $N is replaced by '4'.

$V (list of non keyword arguments):
  In the example, $V is replaced by 'x,run(5)'.  When $V is used within
  double quotes, any instances of '"' or '\' are expanded as for $0.

$v (number of non keyword arguments):
  In the example, $v is replaced by '2'.

$K (list of keyword arguments):
  In the example, $K is replaced by 'title:"Hello Dolly",new:T'.  This
  is useful for passing on all the keywords to a function invoked by the
  macro.  See pre-defined macro colplot() for an example of the use of
  $K.  When $K is used within double quotes, any instances of '"' or '\'
  are expanded as for $0.

$k (number of keyword arguments):
  In the example, $k is replaced by '2'.

$A (CHARACTER vector of all arguments in quotes):
  In the example $A is replaced by vector("x","title:\"Hello Dolly\"",
  "run(5)","new:T").  To print argument 3 to a macro as it appeared in
  the call, instead of print("$3"), you might use print($A[3]).  In
  the example this would print 'run(5)'.  Any instances of '"' or '\' in
  the arguments are replaced by '\"' and '\\'.  When $A is part of a
  quoted string, it is not expanded.

$S (name of macro):
  In the example, $S is replaced by 'mymacro' whether or not it is in
  quotes.

$$ (number unique to macro expansion):
  This expands to a unique two digit number, even in a quoted string.
  The particular integer that replaces $$ remains the same throughout an
  invocation of the macro, but is incremented by 1 for each macro in
  which $$ is used.  It allows you to create temporary variable names
  that are specific to a particular execution of a macro.  For example,
  '@A$$ <- 3' might become '@A52 <- 3' or '@A57 <- 3' depending on when
  and where the macro is executed.  As long as you don't use any
  temporary names ending in two digits, rigorous use of '$$' eliminates
  the possibility of "collisions" between temporary variables names in
  different macros.

  In a macro expanded in-line, the value of $$ starts at 50.  If $$
  reaches 100, all macros abort.  In a macro expanded out-of-line, the
  value of $$ ranges from 0 to 49 and all macros abort if it reaches 50.
  This behavior limits the depth to which macros can be nested or
  recursive macros can call themselves

  Note that each invocation of a macro expanded in-line in a loop will
  have the same value for $$ since it is expanded only once, the first
  time through the loop.  This will usually be the case for macros
  expanded out-of-line, too, but it cannot be guaranteed.

                        Recursive Use of Macros
A macro may invoke itself directly or indirectly up to a maximum depth
of 50, provided some test is included to avoid infinite recursion.  In
practice, current limitations of the parser limit the maximum depth to
around 20.  If you need greater depth, you may be able to attain it
using evaluate().

                      Tips For Good Macro Writing
Because macro arguments are expanded literally, their place holders
should usually be enclosed in parentheses when used in expressions.
For example, use '($1)*($2)' rather than '$1*$2'.

When a macro argument is referred to more than once in a macro it should
usually first be copied to a temporary variable.  For example, a macro
to compute a mean should be defined by
  Cmd> mean <- macro("@x <- $1;sum(@x)/dim(@x)[1]",dollars:T)
rather than by the somewhat simpler
  Cmd> mean <-  macro("sum($1)/dim($1)[1]")
If the latter definition is used, 'mean(boxcox(x[,vector(1,2,4)],.5))'
would result in boxcox(x[,vector(1,2,4)],.5) being evaluated twice.

When a macro is not intended to return a value, end it with ';;'.  For
arcane reasons, this helps MacAnova allocate memory more efficiently.

Use error() to print any messages describing errors that terminate the
macro.  It works similarly to print() but automatically prefaces the
message with "ERROR: ", appends " in macro xxxxx" and produces an error
condition that terminates the macro and returns to the prompt level.

If the error message contains the macro name (dollar symbol $S), use
keyword phrase 'macroname:F' with error() as in
  if ($v != 2){error("$S requires 2 non-keyword arguments",macroname:F)}

See topic error() for more information.

If you use anova(), regress() or any other GLM command in the macro and
you do not want to destroy information from a previous GLM command,
bracket the use of a GLM command by pushmodel() and popmodel() as in the
following fragment:
  if (pushmodel(canpush:T)){pushmodel()}
  anova(@model, silent:T)
  if (popmodel(canpop:T)){popmodel()}

If you want to have the macro run on versions dating prior to the
introduction of pushmodel() and popmodel(), use
  if (alltrue(isfunction(pushmodel),pushmodel(canpush:T))){pushmodel()}
  anova(@model, silent:T)
  if (alltrue(isfunction(popmodel),popmodel(canpop:T))){popmodel()}

                       Functions Useful in Macros
There are several functions whose primary usefulness is within a macro.

Functions anymissing(), isdefined(), ismatrix(), isscalar(),
isvector(), isfactor(), isreal(), ischar(), islogic(), isgraph(),
isstruc() and isfunction() are useful in checking whether
an argument is suitable.

Function keyvalue() interprets arguments that are keyword phrases list
and check their values for appropriateness.

Function argvalue() lets you easily check non-keyword arguments for
appropriateness.

Functions modelvars(), modelinfo(), varnames(), xvariables() and xrows()
allow a macro to do sophisticated computations based on the results of a
previous GLM command.

Functions pushmodel() and popmodel() allow you to save and restore
results from a previous GLM command.

Here is a fragment that might be in a macro whose first argument should
be a REAL scalar and second argument should a CHARACTER vector, and
which should recognize keywords 'down' with T or F for value and 'power'
with positive REAL scalar value.  'down' is optional with default F and
'power' is required.

  @n <- argvalue($1,"sample size","positive integer scalar")
  @labs <- argvalue($2,"labels","character vector")
  @down <- keyvalue($K,"down","TF",default:F)
  @power <- keyvalue($K,"power","positive number")
  if (isnull(@power)){
     error("keyword 'power' is required by $S",macroname:F)
  }
  ....

When the value of an argument doesn't have the specified properties,
argvalue() and keyvalue() immediately terminate the macro.

See macroread() for examples of macros.


Gary Oehlert 2003-01-15