DMDX Help.
Macro Definition Keyword
<MacroDefinition C text>
<md C text>
<macro C text>
M switch alternative. C is the macro to define/re-define and text is the body of the macro. Note that text is not delimited as in an M switch definition if there is white space after C. Should a non-space character follow C then it will be treated as a delimiter and a closing delimiter will also have to be present at the end of text (before the closing > of the keyword) as in the original M switch, for example:
<md m+macro body+>
Versus:
<md m macro body>
To use a macro expansion the sequence tilde macro character is used, ~m for example. Macros can be nested within a macro definition with the use of two tilde characters, for example:
<md b body> <md m macro ~~b>
Macro b could be re-defined later and macro m will then expand with the new definition of b.
NOTE:- the keyword variant of macro expansion is severely limited (at least until I write some serious code to expand the keyword parser) because you can't have angle brackets nested with a keyword definition, ie you can't have keywords in a keyword macro definition, you have to use the original DMTG switch form of macro expansion. Seeing as I can't actually find a definition if the original macro switch I include one here. The macro switch takes the form:
mCDmacro bodyD
The m character indicates that a macro definition follows. The C character defines which macro is to be defined or re-defined, valid characters are 0..9 A..Z (others probably work if they have no special meaning elsewhere). The D characters are delimiters delimiting the actual text that the macro invocation will expand to, usually plus signs are used, anything that has no special meaning elsewhere. White space between
the
m, C
and D characters is not allowed. To use a macro expansion the sequence tilde macro character is used, ~m for example. Macros can be nested within a macro definition with the use of two tilde characters, for example:
mb+body+ mm+macro ~~b+
Macro b could be re-defined later and macro m will then expand with the new definition of b.
Undefined macros don't
expand (the tilde and the character remain as typed) with the exception
(starting in version 3.3.1.0 of DMDX) of macro S which will expand to the
subject ID. If no subject ID has been specified use of ~S without defining
it beforehand will result in ~S being removed. Sure hope people weren't
relying on that but it seems like a sufficiently unusual thing that I can ignore
my usual rule of not changing how earlier item files behave and instead people
can use custom file names for individual subjects without editing item files or
just over writing a common file all the time like this:
+1 "You or not you?" / g "picture~S";
NOTE:- You can't define macros in items that are comments (they aren't
parsed so the parser doesn't see the macro definition), nor can you define a
macro in the item it's expanded in as the macro expansion occurs before the item
is parsed (had to go and change the examples because of that one, oops).
NOTE:- Using characters 0..9 for macros if using the <SkipDisplay> CR indicator ~ will result in unwanted macro expansion unless there is a space between the tilde and digit or ~~ is used (where the macro code will collapse it to ~).
See the dynamic item content note.
NOTE:-
You also can't have macros across item boundaries. The macro definition
has to be in one item and a semi-colon is an end of item no matter where it
occurs so you'd never be able to get one into the macro definition. Let
alone the fact that would probably bust DMDX's item_read() function...
NOTE:- Macros used inside quotes can fall afoul of RTF control words between the
tilde and the macro character stopping the macro expansion code from seeing the
sequence as macro use. This can happen if you edit an expansion sequence
in Word (say by changing the macro character) and even though there's no
formatting information in there Word can still stick change tracking information
in there. Should this occur retyping the whole macro sequence should fix
it. Macros used elsewhere in items are fine, it's only within the double
quoted text field that this occurs.
NOTE:- Considerable care must be used if you're going to have a macro expand as
an item number as the branching code does not expand macros when it's
looking for an item to branch to. Macros only get expanded once it is
decided that an item is going to be executed. This can have unintended
consequences as I recently discovered where I was using an alpha macro (say ~A)
for an item number in a subroutine but as the initial branch over the subroutine
was executing I'd get a syntax error about a missing item number. The
solution was to use a numeric macro instead (say ~9) that passes the syntax
requirements for the branch but at a later date when actually in use expands to
the desired item number. One could also get around it with compound item
number (say +0~A). But you can't use macros in item numbers as branch
targets.NOTE:- Macros will require a little
extra attention when the
Unicode code path is on.
Simpler example
A slightly simpler yet still pretty cool use
of macros emerged recently with the need to scramble and display components in the
corners of a display. Using
<xyjustification 1> and using one macro to determine the definition of
another this becomes a relatively straight forward item file where the
components of the display (the item 1000s) are scrambled all over the place.
The <emit> keywords are used to keep
track of what was eventually presented, each component item defines some macro
determined by macro O allowing them to be put into the
corners for presentation. Note that we're using the tilde in two totally
different contexts in these examples, where it occurs at the start of an item
it's a
<SkipDisplay>
indicator, elsewhere it's a macro reference (as noted above):
$~1 mO+A+;$
~1001 m~O+ "text" <emit text> +;
$~1 mO+B+;$
~1002 m~O+ g "filename" <emit filename> +;
$~1 mO+C+;$
~1003 m~O+ "more text" <emit more text> +;
$~1 mO+D+;$
~1004 m~O+ g "anotherfilename" <emit anotherfilename> +;
$+100 ~A <xy .25, .25> , ~B <xy .75, .25> , ~C <xy .25, .75> , ~D <xy .75, .75>
* ;$
Another neat example
Someone recently wanted to be able to have
the subject type in their name and to be able to present it later on (for
a me-not me IAT) so I came up with this bit of macro code that gathers a name
(even allows backspacing) without using the
<ZillionTypedResponses>
code (that wouldn't let you present the name later anyway, not without
modifications to DMDX). Macro N is the name that's being assembled (as
well as the resultant name), macros A through E track previous versions of macro
N for backspacing (if they back space more than four times it'll just erase
everything and start over). Basically we do a multiway branch based on the
key typed and add the relevant key to the end of macro N. If they hit the
back space we undo one level of backspacing and exit if they hit enter:
<ep> <d 0> f1 t100000 <vm desktop>
<nfb>
<cr> <id keyboard> <umb>
<mpr +a>
<mpr +b> <mpr +c> <mpr +d> <mpr +e> <mpr +f>
<mpr +g> <mpr +h> <mpr +i> <mpr
+j> <mpr +k> <mpr +l>
<mpr +m> <mpr +n> <mpr +o> <mpr +p> <mpr +q> <mpr +r>
<mpr +s> <mpr +t> <mpr +u> <mpr +v> <mpr +w> <mpr +x>
<mpr +y> <mpr +z> <mpr
+enter> <mpr +backspace> <mpr +space> <eop>
~1 mN++ mA++ mB++ mC++ mD++
mE++;
~10 mA+~B+ mB+~C+ mC+~D+ mD+~E+ mE+~N+;
+20 * "Enter your name:
~N" <mwb +A,101 +B,102 +C,103
+D,104 +E,105 +F,106 +G,107 +H,108 +I,109 +J,110 +K,111 +L,112
+M,113 +N,114
+O,115 +P,116 +Q,117 +R,118 +S,119 +T,120 +U,121
+V,122 +W,123 +X,124 +Y,125
+Z,126 +Backspace,30 +space,35 +Enter,199>;
~30 mN+~D+ mE+~D+ mD+~C+ mC+~B+
mB+~A+ mA++ <bu -20>;
~35 mN+~N +
<bu -10>;
~101 mN+~NA+ <bu -10>;
~102 mN+~NB+ <bu -10>;
~103 mN+~NC+ <bu
-10>;
~104 mN+~ND+ <bu -10>;
~105 mN+~NE+ <bu -10>;
~106 mN+~NF+ <bu
-10>;
~107 mN+~NG+ <bu -10>;
~108 mN+~NH+ <bu -10>;
~109 mN+~NI+ <bu
-10>;
~110 mN+~NJ+ <bu -10>;
~111 mN+~NK+ <bu -10>;
~112 mN+~NL+ <bu
-10>;
~113 mN+~NM+ <bu -10>;
~114 mN+~NN+ <bu -10>;
~115 mN+~NO+ <bu
-10>;
~116 mN+~NP+ <bu -10>;
~117 mN+~NQ+ <bu -10>;
~118 mN+~NR+ <bu
-10>;
~119 mN+~NS+ <bu -10>;
~120 mN+~NT+ <bu -10>;
~121 mN+~NU+ <bu
-10>;
~122 mN+~NV+ <bu -10>;
~123 mN+~NW+ <bu -10>;
~124 mN+~NX+ <bu
-10>;
~125 mN+~NY+ <bu -10>;
~126 mN+~NZ+ <bu -10>;
~199;
+1000
"Me or not me? ~N" *;
Setting a macro's value from a counter
In a similar vein to the typed responses
example you can use a looping subroutine to set a macro to a counter's value.
This is really handy if there's some keyword that you want a parameter set from
an expression and DMDX doesn't allow it otherwise:
<vm desktop> <id "keyboard">
~01 <set
c1 = random(10000)> <call 10>;
02 "macro
is ~C, counter is " <apc 1> <bu
-1> ;
~10 mC++ <set c99=c1>;
~99 <set c98=(c99 % 10)+100><ib
98>;
~100 mC+0~C+ <bu 110>;
~101 mC+1~C+ <bu 110>;
~102 mC+2~C+ <bu
110>;
~103 mC+3~C+ <bu 110>;
~104 mC+4~C+ <bu 110>;
~105 mC+5~C+ <bu
110>;
~106 mC+6~C+ <bu 110>;
~107 mC+7~C+ <bu 110>;
~108 mC+8~C+ <bu
110>;
~109 mC+9~C+;
~110 <set c99=c99 / 10> <bi -99, c99 .gt. 0>;
~111
<return>;
Other neat examples
The Dynamic Item Content help also
uses macros extensively (including a neat Ultimatum game script).
DMDX Index.