Macros are more difficult to debug than regular syntax, because one cannot
"walk" the macro step by step. We have to call it an let it go. The first step
to debug a macro is to run the command SET MPRINT=yes /PRINTBACK=yes.
This allows you to "see" the macro in the Output window.
Another useful method is to insert SAVE OUTFILE commands in strategic places and look
at these files after the macro has finished to see where things starts to go off tracks.
Remember that (most of the time) computers do what you tell them to do, not what you
want them to do :-)
I just spent a day being
fooled by a feature of the LOOP structure, namely
the function of the (implicit) MXLOOPS variable. Perhaps what MXLOOPS
does is well known to other syntax users, but I suspect not.
MXLOOPS is the maximum number of iterations for an un-indexed use of LOOP.
MXLOOPS defaults to 40, but can be set using SET MXLOOPS = n .
This seems unproblematic, but note that is in effect even when a
loop is controlled by an IF-Clause.
See what happens with the following syntax:
COMPUTE TRIALS = 100 .
COMPUTE #I = 1 .
IF RV.UNIFORM(0,1) < 0.0001 X = 1.
COMPUTE #I = #I + 1.
END LOOP IF (#I > TRIALS) or (X = 1) .
Despite the intent of this loop to run for 100 trials or until X = 1,
whichever comes first, it will never run more than MXLOOPS times.
With the default of MXLOOPS = 40, it will run only 40 times maximum.
In other words, the apparent user control of the loop is overridden
The solution is to take active control of MXLOOPS by setting your own
"infinite loop protection" somewhere prior to the loop in question,
e.g. SET MXLOOPS = 1E8 for the brave.
Say you define a macro as follows:
DEFINE !mymacro (age=!TOKENS(1) /!POS=!CMDEND)
and you call the macro as follows:
!mymacro age=18 29 35 78.
The macro will not work because the parameter "age" will NOT be defined! Yes the
macro call contains age=18 and this appears to define the parameter age. The problem is
that, by definition , the first parameter is !POS, moreover, in this
case, !POS goes up to the command terminator; hence !POS = "age=18 29 35 78".
The macro parser does not see the parameter "age".
If some of the parameter values feed to the macro are combinations of
numbers and letters such as
- numbers followed by strings (for instance 123ab), or
- numbers followed by strings followed by numbers (for instance 123ab21),
- underscore followed by a number (for instance _1)
It is likely that the "odd feature" of the macro parser is the culprit. See this file for more explanations.