DMDX Help.


Macro Definition Keyword

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

variant:
Macro Operations

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

variant:
Macro Command

<macro yikwid>

special macros:
S
.zilliontext.
.tcpipreply.



    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.  name is the macro to define/re-define and
text is the body of the macro. 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 waaaay down below and the 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 (a somewhat perilous thing to do that I keep running into so others must hit it, there is however one legitimate instance where it's valid to do and that's where you're redefining a macro based on it's previous content).  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 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. 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.  Using variable length macro names that might be:

<md .body. body> <md .macro. macro ~~.body.>

    NOTE:- the keyword variant of macro expansion is somewhat limited 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 (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) . 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.   Updated with a variable length macro name the example above would be:
 
m.name.Dmacro bodyD
 
    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";

    Not that S is really a reserved macro 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 however as of version 6.0.2.0 of DMDX there's a reserved variable length 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.

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 (all the more so when you're using variable length names).  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 or do what I 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 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.  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).  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 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 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).  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 new set operation (see below) 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)::

~1000 <macro set .counter. = 1>;
~1005 <set c~.counter. = 0> <macro set .counter. = ~.counter. + 1>;
~1006 <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>.



Macro Stack Operation Keywords

<macro pop name, N>
<macro push name, N>
<macro pop name, counterN>
<macro push name, counterN>
<macro pop name, cN>
<macro push name, cN>

    The push and pop operations treat previously defined macro name as a stack (leftmost character being the top of the stack) and push the contents of counter N on to that macro (so it becomes the leftmost character) or pop the contents of that macro (left most character) into counter N.  Most of the time counter N will contain the ASCII decimal value of character (so for instance A is 65 and Z is 90 although these days with version 6.1.0.1 having character constants in expressions you should only have to look up things like DMDX special characters) being pushed onto the macro or popped off.  The only real exception to this would be when characters are popped off an already empty macro in which case the counter will wind up with the value zero.  Note, be careful not to push a zero onto a macro as that will truncate the macro and you won't be able to pop it off as while we are treating the macro as a stack it's still a C character string that is NULL terminated (more on that below).  So any number of attempts to pop characters off an empty stack are legal, indeed it's the only way to see if the stack is empty (not till I add a strlen operator anyway). 

    There are countless applications of these operations, the scope of which I can only begin to touch upon.  Initially this was implemented to allow an anagram task to be coded (below) in DMDX however in doing that I realized you could recode the macro based typing (if one wasn't already using the new <prose> keyword anyway) and I notice in scrolling down here that the setting a macro's value from a counter example could similarly be simplified.  The push and pop operations are similarly indispensible in the tcpip reply parsing example in the <send> and <storecoords> keyword documentation.

Anagram task

    The principal feature of the anagram task that sets it apart from all other typing code in DMDX is that requires the set of characters that can be typed to be constantly adjusted to fit the list of letters remaining in the source word of the anagram that haven't been typed yet.  And this, as it turns out is quite a big deal.   Besides needing some method of manipulating macros so the contents can be parsed (much to my surprise that was the easy part) my first inclination was to limit characters using the macro typing example above with and only mapping the relevant characters.  I could probably have gotten away with <ZillionTypedResponses> I suppose but my mind wasn't going there at the time.  The problem with limiting the mapped characters turns out to be the <MultiwayBranch>, any character it branches on must be mapped as an input and of course that's not the case when we're dynamically switching them in and out.  So I had to use counters to control what characters can be typed.  And by the time I had that all working something was seriously awry (one of the reasons the branch diagnostics got expanded) with the backspacing.  Fortunately I realized the rather primitive back spacing in the original macro typing example (where it keeps track of the last four states of the typing) could be replaced with a couple of stack operations (pulling everything off onto another macro (now reversed) and then pushing it all back after throwing out the last character in items 2030 through 2080).  If we were using right to left typing of course one could just pop the top character off the macro.  I dimly remember a language from the late 70s called Forth that was similarly stack based and being appalled at the weird array of options it offered (like the example mentioned above popping a whole stack) but of course they make perfect sense once you start using it.  I think someone once wrote a whole OS in Forth just to prove it was actually useful.

    One thing I realized as I was writing the portion of the script that handles the backspace was that the code that keeps popping characters off one macro and onto another (items 2040 and 2050) was that you can't gaily pop characters off and push them on the other macro and then check for the zero marking the end of the string.  As soon as you push that zero onto the other string you've truncated it.  So there are tests in items 2040 and 2070 that check for the zero before pushing it onto another macro in the next item.  One's immediate instinct is to just have a one item loop that does it all, however this won't work unfortunately, even as elegant as it is. 

<ep>
<vm desktop>
<msfd 1000> <nfb> <cr> <t 100000>
<id keyboard> <!branchdiagnostics>
<eop>
0 mZ+ANAGRAM+ "start" <call 1000>;
1 mZ+ABCDEFGHIJKLMNOPQRSTUVWXYZ+ <call 1000>;
0"end"l;

~1000 mN++ <set c100=0> <set c101=0>
<umb> <mpr +enter> <mpr +backspace>
<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>;
~1010 <set c65 = 0> <set c66 = 0> <set c67 = 0> <set c68 = 0> <set c69 = 0> <set c70 = 0> <set c71 = 0> <set c72 = 0> <set c73 = 0>
<set c74 = 0> <set c75 = 0> <set c76 = 0> <set c77 = 0> <set c78 = 0> <set c79 = 0> <set c80 = 0> <set c81 = 0> <set c82 = 0>
<set c83 = 0> <set c84 = 0> <set c85 = 0> <set c86 = 0> <set c87 = 0> <set c88 = 0> <set c89 = 0> <set c90 = 0>;
~1100 mQ+~Z+ mR+~N+ <! Loop through letters of anagram looking for letters used already>;
~1200 mO+~R+;
~1210 <macro pop Q, 100> <bi 2000, c100 .eq. 0> <! Looked at all of anagram>;
~1220 <macro pop O, 101> <bi 1300, c101 .eq. 0> <! Looked at all of typed>;
~1230 <bi -1220, c101 .ne. c100>;
~1235 mS++;
~1240 <macro pop R, 101> <bi 1260, c101 .eq. c100> <! Occurs in both so already used so remove from R list>;
~1250 <macro push S, 101> <bu -1240> <! Yes, if it's not in the list this will loop forever>;
~1260 mR+~R~S+ <bu -1200>;
~1300 <set c100 = c100 + 1300> <ib 100> <! Char must be valid to use>;
~1365 <set c65 = 1> <bu -1200>;
~1366 <set c66 = 1> <bu -1200>;
~1367 <set c67 = 1> <bu -1200>;
~1368 <set c68 = 1> <bu -1200>;
~1369 <set c69 = 1> <bu -1200>;
~1370 <set c70 = 1> <bu -1200>;
~1371 <set c71 = 1> <bu -1200>;
~1372 <set c72 = 1> <bu -1200>;
~1373 <set c73 = 1> <bu -1200>;
~1374 <set c74 = 1> <bu -1200>;
~1375 <set c75 = 1> <bu -1200>;
~1376 <set c76 = 1> <bu -1200>;
~1377 <set c77 = 1> <bu -1200>;
~1378 <set c78 = 1> <bu -1200>;
~1379 <set c79 = 1> <bu -1200>;
~1380 <set c80 = 1> <bu -1200>;
~1381 <set c81 = 1> <bu -1200>;
~1382 <set c82 = 1> <bu -1200>;
~1383 <set c83 = 1> <bu -1200>;
~1384 <set c84 = 1> <bu -1200>;
~1385 <set c85 = 1> <bu -1200>;
~1386 <set c86 = 1> <bu -1200>;
~1387 <set c87 = 1> <bu -1200>;
~1388 <set c88 = 1> <bu -1200>;
~1389 <set c89 = 1> <bu -1200>;
~1390 <set c90 = 1> <bu -1200>;

+2000 <delay 2> @-2 "~z", <cco> "Enter the anagram:", @2 "~N" <mwb +A,2101 +B,2102 +C,2103
+D,2104 +E,2105 +F,2106 +G,2107 +H,2108 +I,2109 +J,2110 +K,2111 +L,2112
+M,2113 +N,2114 +O,2115 +P,2116 +Q,2117 +R,2118 +S,2119 +T,2120 +U,2121
+V,2122 +W,2123 +X,2124 +Y,2125 +Z,2126 +Backspace,2030 +Enter,2199>;
~2020 <bu -2000> <! item is critical if people use a lower timeout>;
~2030 mO++ <! Backspace by pulling everything off, discarding last and putting them back>;
~2040 <macro pop N, 100> <bi -2060, c100 .eq. 0>;
~2050 <macro push O, 100> <bu -2040>;
~2060 <macro pop O, 100>;
~2070 <macro pop O, 100> <bi -1010, c100 .eq. 0>;
~2080 <macro push N, 100> <bu -2070>;
~2101 <bi -2000, c65 .eq. 0>;
~1 mN+~NA+ <bu -1010>;
~2102 <bi -2000, c66 .eq. 0>;
~1 mN+~NB+ <bu -1010>;
~2103 <bi -2000, c67 .eq. 0>;
~1 mN+~NC+ <bu -1010>;
~2104 <bi -2000, c68 .eq. 0>;
~1 mN+~ND+ <bu -1010>;
~2105 <bi -2000, c69 .eq. 0>;
~1 mN+~NE+ <bu -1010>;
~2106 <bi -2000, c70 .eq. 0>;
~1 mN+~NF+ <bu -1010>;
~2107 <bi -2000, c71 .eq. 0>;
~1 mN+~NG+ <bu -1010>;
~2108 <bi -2000, c72 .eq. 0>;
~1 mN+~NH+ <bu -1010>;
~2109 <bi -2000, c73 .eq. 0>;
~1 mN+~NI+ <bu -1010>;
~2110 <bi -2000, c74 .eq. 0>;
~1 mN+~NJ+ <bu -1010>;
~2111 <bi -2000, c75 .eq. 0>;
~1 mN+~NK+ <bu -1010>;
~2112 <bi -2000, c76 .eq. 0>;
~1 mN+~NL+ <bu -1010>;
~2113 <bi -2000, c77 .eq. 0>;
~1 mN+~NM+ <bu -1010>;
~2114 <bi -2000, c78 .eq. 0>;
~1 mN+~NN+ <bu -1010>;
~2115 <bi -2000, c79 .eq. 0>;
~1 mN+~NO+ <bu -1010>;
~2116 <bi -2000, c80 .eq. 0>;
~1 mN+~NP+ <bu -1010>;
~2117 <bi -2000, c81 .eq. 0>;
~1 mN+~NQ+ <bu -1010>;
~2118 <bi -2000, c82 .eq. 0>;
~1 mN+~NR+ <bu -1010>;
~2119 <bi -2000, c83 .eq. 0>;
~1 mN+~NS+ <bu -1010>;
~2120 <bi -2000, c84 .eq. 0>;
~1 mN+~NT+ <bu -1010>;
~2121 <bi -2000, c85 .eq. 0>;
~1 mN+~NU+ <bu -1010>;
~2122 <bi -2000, c86 .eq. 0>;
~1 mN+~NV+ <bu -1010>;
~2123 <bi -2000, c87 .eq. 0>;
~1 mN+~NW+ <bu -1010>;
~2124 <bi -2000, c88 .eq. 0>;
~1 mN+~NX+ <bu -1010>;
~2125 <bi -2000, c89 .eq. 0>;
~1 mN+~NY+ <bu -1010>;
~2126 <bi -2000, c90 .eq. 0>;
~1 mN+~NZ+ <bu -1010>;
~2199 <return>;
!stop DMDX from exiting;

Macro comparison example

    Perhaps more useful, you can use the stack operators to compare two macros if you say wanted to compare subject input (using a macro input routine above or the <prose> keyword) to a specific value:

<ep>
<fd 30> <vm desktop> <id keyboard>
<!branchdiagnostics>
<eop>

0 "macro comparison demo";
~1 <md .strA. targeter> <md .strB. target> <call 1111>;
~1 <bi 20, c1111 .ne. 0>;
10 "strings not the same" <bu 30>;
20 "strings the same";
~30 <md .strA. target> <md .strB. target> <call 1111>;
~1 <bi 20, c1111 .ne. 0>;
10 "strings not the same" <bu 30>;
20 "strings the same";
30 "done" L;

~1111 <set c1112 = 0> <set c1113 = 0>;
~1120 <macro pop .strA., 1112> <macro pop .strB., 1113> <bi 1130, c1112 .ne. c1113>;
~1124 <bi -1120, c1112 .ne. 0> <! if end of one string must be end of both as the values are the same so must be success otherwise do next char>;
~1126 <set c1111 = 1> <return>;
~1130 <set c1111 = 0> <return>;

! comment to stop dmdx auto exiting when previous item executes;


Macro Set Keyword

<macro set name = expression>

   Rounding out my wish list of features since forever is the set operator added in version 4.3.0.0.  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 however it still requires a little care in using it due to the fact (mentioned above) about macros being expanded at item read time and only defined later as the item is parsed.  So the following for instance does not result in counter 49 getting assigned "49" and instead if macro .body. hadn't been defined before would get flagged as a syntax error:

~1 <macro set .body. = 7*7> <set c49 = ~.body.>;

   Instead the correct solution is to use two items (note they're skip display items so they execute almost as fast as one does):

~1 <macro set .body. = 7*7>;
~1
<set c49 = ~.body.>;


Macro Fixed Point Set Keyword

<macro fxpset N name = expression>

   Back in the dark ages when I first added the expression evaluator to DMDX there was no call for floating point capability at all, however as DMDX has evolved there are now several cases where they're used, screen coordinates can now be between zero and one and both font and bitmap multipliers can be fractional as well and when using <macro set> to manipulate them one needs to prepend leading zeroes to the fractional component of the value as shown in the Stretch Blt modes discussion.  This is a right pain and having recently come across another example that needs this in spades we now have the fxpset option.  Here one decides how many fixed decimal places one is going to use and passes that as N and multiplies the values in the expression by a corresponding amount (so for three decimal places you multiply by 1000).  So from my Corsi Block Tapping task where we have nine different targets on the screen and the coordinates of them have been generated randomly in counters 100 to 117 (a pair of them for each target, first being X and second being Y) with values between 0 and 100 where in order to display them they need to be 0.00 to 1.00 we have:

~1 <macro fxpset 2 .b1x. = c100> <macro fxpset 2 .b1y. = c101>
<macro fxpset 2 .b2x. = c102> <macro fxpset 2 .b2y. = c103>
<macro fxpset 2 .b3x. = c104> <macro fxpset 2 .b3y. = c105>
<macro fxpset 2 .b4x. = c106> <macro fxpset 2 .b4y. = c107>
<macro fxpset 2 .b5x. = c108> <macro fxpset 2 .b5y. = c109>
<macro fxpset 2 .b6x. = c110> <macro fxpset 2 .b6y. = c111>
<macro fxpset 2 .b7x. = c112> <macro fxpset 2 .b7y. = c113>
<macro fxpset 2 .b8x. = c114> <macro fxpset 2 .b8y. = c115>
<macro fxpset 2 .b9x. = c116> <macro fxpset 2 .b9y. = c117>;
0 <dfm ~.fm. STAT> <dwc 0,0,255 STAT> <xy ~.b1x., ~.b1y.> "■", <xy ~.b2x., ~.b2y.> "■", <xy ~.b3x., ~.b3y.> "■", <xy ~.b4x., ~.b4y.> "■",<xy ~.b5x., ~.b5y.> "■",<xy ~.b6x., ~.b6y.> "■",<xy ~.b7x., ~.b7y.> "■",<xy ~.b8x., ~.b8y.> "■", <wc 255,255,0> <xy ~.b9x., ~.b9y.> "■";

   Note that we are deliberately using two items, one to set the macros and one to use them as per the usual you can't define a macro and use that new value in the same item.




DMDX Index.