DMDX Help.


Macro Definition Keyword

<MacroDefinition name text>
<md name text
>
<macro name text
>

variants:
Macro Operations

<macro pop name, designator>
<macro push name, designator>
<macro set name = expression>
<macro fxpset N name = expression>

macro name designators:
c (a single ASCII char)
.macroname.

counter designators:
counter.countername.
c.countername.
counterN
cN
N

deprecated variant:
<macro yikwid>

special macros:
S
.zilliontext.
.tcpipreply.
.$???.
.dataloggingx.
.dataloggingy.


    M switch alternative that allows the definition and manipulation of macros.  For a discussion on the uses of macros see the Macro section in the built in introduction.

   As of version 4.3.0.0 of DMDX macro names can in addition to the traditional single character C can also now be dot delimited names (they must begin with and end with a period).  Maximum name length (including dots) is 40 characters and the names are case insensitive and counter namespace is independent from macro namespace so you can quite happily have both a counter and macro with the same name that don't interfere with each other in any way.  name is the macro to define/re-define and
text is the body of the macro.  As of version 6.4.0.0 of DMDX macro namespace includes UTF-8 characters as long as the Unicode option is on.  The push and pop operation variants operate on existing macros (stack push and pop operations to counter N) whereas the set operation will create a new macro if it didn't already exist and set it to the ASCII equivalent of the result of expression (see <set> for expression details).  Operations are detailed on the macro set page and the macro stack operators page) and the deprecated macro command yikwid is to suppress the warning (yes I know what I'm doing) about a macro's manipulation and usage in the same item.  As of version 6.5.0.0 of DMDX yikwid is all but superfluous as you can use the in place back tick (`) expansion character that expands macros as the item is parsed instead of the tilde expansion character that happens before parsing.  Note that text is not delimited as in an M switch definition if there is white space after the macro name, see DMTG macro switch definition below.  Should a non-space character follow name 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 or as of version 6.5.0.0 of DMDX the back tick macro character can be used, `m for example, the difference being that tilde macro sequences expand before the item is parsed (so you can't define a tilde macro in the same item that you use it) whereas the back tick version of a macro expands in place as the item is being parsed so defining a macro and using it in the same item is possible. Note that because in place expansion occurs during parsing it's not available when DMDX buffers file name resources before an item is parsed so the code will retry the operation at parse time and you'll see messages in the diagnostics to that effect. 

    Macros can be nested within a macro definition with the use of two tilde (or back tick) 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.  Using variable length macro names that might be:

<md .body. body> <md .macro. macro ``.body.>

    As of version 6.4.0.0 of DMDX macro dot delimited names can be Unicode characters if the Unicode option is in effect however note the converse is not the case, single character macro names are limited to ASCII characters as the Unicode characters are represented with UTF-8 internally and multiple bytes are used to encode them (in the following that omkara (U+950) character takes three bytes to encode so the name of that macro is actually five bytes out of the maximum of 40 instead of the three it looks like and if we'd used the folding hands emoji (U+1F64F) as the macro name it would be six bytes as all emoticons take four bytes to encode in UTF-8).  Not only that but the body can be Unicode without having to resort to using double quotes (RTF formatting however will not be preserved).  For instance the following is perfectly valid (in the past the RTF encoding the omkara and the folding hands emoji would have been stripped out because they weren't in a text segment):

<md .ॐ. 🙏> "`.ॐ."

    If you wanted to include formatting in the body of the macro (here our folding hands emoticon is in a larger font size) you would have to surround it with double quotes but be aware that the quotes are then part of the macro body when there's a space after the macro name so it's expansion can't have quotes around it, instead you'd have to use the second example where the quote becomes a macro delimiter (because there's no space after the macro name):

<md .ॐ. "🙏"> `.ॐ.
<md .ॐ."🙏"> "`.ॐ."

    Note that the keyword variant of macro expansion is somewhat limited because you can't have angle brackets nested within 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 (as of version 6.1.0.0 of DMDX you can in fact have angle brackets in a double quoted string in a keyword, however that's going to have quotes and RTF formatting in it so it's not a good idea to be using that method to get keywords into a macro definition as when expanded it will cause syntax errors).  So until I write some serious code to expand the keyword parser you're stuck with the DMTG form of macro definitions for macros with keywords in them and seeing as I can't actually find a definition of 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 and that doesn't occur in the body of the macro. White space between the m, C and D characters is not allowed.   As noted above that there's nothing stopping you from using a double quote as a delimiter if you want to include RTF text formatting in a macro.  Updated with a variable length macro name the example above would be:
 
m.name.+macro body+
 
    Undefined macros don't expand (the tilde or back tick and the selector character or dot delimited name 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 as the subject ID is an empty string and the macro expansion code dutifully replaces the `S with nothing...  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";

    Not that S is really a reserved macro per se as the subject ID is only accessible if you deliberately make use of it's undefined value and once you've defined it it's yours to play with as you please.  However as of version 6.0.2.0 of DMDX there's a reserved long macro name macro .zilliontext. that's set to the typed text in the <ZillionTypedResponses> mode and it will get overwritten every time there's a new typed response.  And as of 6.1.0.0 the tcpip input device will assign any received data to the .tcpipreply. macro.  In addition as of version 6.1.6.0 of DMDX any long macro name beginning with a $ (so .$a0t. for instance) will be cleared every time <prose cursor> is used -- hopefully no one was using those on account of $ being a scramble delimiter, if not, sorry about that...  The last data point in any <2Did> logging in an item is saved in the macros .dataloggingx. and .dataloggingy..

NOTE:- You can't define a macro in the item it's expanded in if you're using the lagacy tilde macro expansion character as the macro expansion occurs before the item is parsed however using the new in place back tick (`) expansion character introduced in version 6.5.0.0 of DMDX instead of the tilde you can.  In addition if you are using in place expansion it can be a bit of a challenge to determine exactly what hit the fan when so when branch diagnostics are on and you use a back tick in an item DMDX now emits the item in the diagnostics twice, once after any tilde expansion is done before parsing and then again after item parsing (if you use in place expansion without branch diagnostics you just get to see post in place expansion results).  Also graphic, sound and digital video file names that are expanded by an in place macro will be slightly slower to load as the routine that preloads those files immediately after the last item executes won't have had the macros expanded so the preloading will fail and DMDX will retry loading those files after it's been parsed.  This should still be some time before the resources are needed and machines these days are blindingly fast compared to what was available when I wrote the preloading code so it shouldn't be a problem.

NOTE:- Using characters 0..9 for macros if using the <SkipDisplay> CR indicator (which is a tilde) ~ 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:- Prior to version 6.1.5.2 of DMDX macros used inside quotes could 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 (all the more so when you're using variable length names).  I also saw this happen when using ~~ in a quoted segment.  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 and you're not prepared to update DMDX to version 6.1.5.2 or later retyping the whole macro sequence should fix it or do what I used to do and that is cut the text out into a text editor (I use TextPad but Notepad probably words as well) and then paste it back in to Word.  Opening and saving the file in WordPad similarly strips out all the Word guff.  Macros used elsewhere in items are fine, it's only within the double quoted text field with old versions of DMDX that this occurs.

NOTE:- Care must be used if you're going to have a macro expand as an item number as the branching code can get it's knickers in all kinds of knots when it's looking for an item to branch to and that item number changes.  Basically you can have data gathering items with macros for item numbers (this works well) but you can't branch to an item that has a macro for an item number unless that item number doesn't change.  Additionally when using macros for data gathering items some care must be made to make sure the macro is defined before DMDX ever parses the item -- for instance if it's in a subroutine that's being branched over initially the macro will have to be defined as I discovered when I was building the Ultimatum Game item file using an alpha macro (in that case ~I) 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 as ~I when I is not defined is left as ~I which is indeed an invalid item number.  The solution was to simply define that macro as some dummy number before the subroutine was skipped over and then when actually in use define it to be the desired item number.  One could also get around it with compound item number (say +0~I).

NOTE:- Macros used to require a little extra attention when the Unicode code path is on, however I'm pretty sure as of DMDX version 6.4.0.0 they're all history now.  Not only that in combination with the aforementioned Word idiocy (since obviated with DMDX 6.1.5.2) I once saw a ~~ sequence with Word control words between the two and that upset the Unicode sequence so it was now out of phase with the byte order and it produced a string of mojibake.  Pouring the item file through WordPad or retyping the ~~ solved the problem, as of 6.1.5.2 hopefully the problem has gone away for good.


Examples

    These examples were largely crafted eons ago before the in place back tick macro expansion code was written so a lot of them have got tilde lead ins, if you care to use them there's probably no harm in replacing them with back ticks (indeed the introduction has a version of the corner scrambling using back ticks) but you're probably not gaining anything either as the work has already been done to keep the definitions in items before their use and I'm not about to just arbitrarily do so without testing the results (I have however given back tick alternatives in some of them).
 
    A simple yet still pretty cool use of macros emerged a couple of decades ago with the need to scramble and display components in the corners of a display.  Using
<xyjustification CENTER> 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, typing with macros

    Someone quite some time ago 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 (it even allows limited backspacing) without using the <ZillionTypedResponses> code (at the time .zilliontext. hadn't been implemented).  Even though it has since been superseded by the <Prose> keyword it is still a nice example of the surprising things you can do with macros.  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.  Note that <delay 0> is critical here, without it you're waiting for the default delay every character you type for the display to update.  If you don't use a <delay 0> in the parameters use a <delay 2> in item 20 instead (a delay of zero there will just get you constant display errors).

<ep> <delay 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>;
~29 <bu -20> <! item is critical if people use a lower timeout>;
~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

    While superfluous given the set operation (see the Macro Set Operations page) in version 4.3.0.0 of DMDX this still serves as a useful example even if you would never want to use it anymore.  In a similar vein to the typed responses example you can use a looping subroutine to set a macro to a counter's value:

<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>;

Iterating counters

    Perhaps more useful, you can use the previous subroutine to iterate over a range of counters effectively treating them as an array, here we set up 100 counters easily:

~1000 <set c1 = 1>;
~1005 <call -10>;
~1006 <set c~C = 0> <inc 1> <bi -1005, c1 .le. 100>;

    Of course using the set operation this becomes:

~1000 <set c1 = 1>;
~1005 <macro set C = c1>;
~1006 <set c~C = 0> <inc 1> <bi -1005, c1 .le. 100>;

 And on top of that you can also dispose of  the counter to control the iteration as well.  Note this is one the few times you can actually set a macro to some value and use it in the same item however the test of course has to go in the next item (so this is an item file that you'd probably want to set <macro yikwid> to suppress the warnings but rather than do that I'd use the back tick example that follows):

~1000 <macro set .counter. = 1>;
~1005 <set c~.counter. = 0> <macro set .counter. = ~.counter. + 1>;
~1006 <bi -1005, ~.counter. .le. 100>;

 And as DMDX evolved and the back tick in place macro expansion code was written that whole thing can happily be done with two items:

~1000 <macro set .counter. = 1>;
~1005 <set c`.counter. = 0> <macro set .counter. = `.counter. + 1>
        <bi -1005, `.counter. .le. 100>;


    The Probabilistic Selection Task also uses iteration to randomize a list.

 

Other neat examples

    The Dynamic Item Content help also uses macros extensively (including a neat Ultimatum game script) and the continue clock on examples use a macro to start items with a <co> and continue them with <cco>.




DMDX Index.