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.