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.