Scripting and Macros
Mesquite's scripting system allows modules and other objects
to be scripted using text commands. Mesquite uses scripting by
text commands as follows:
- When a menu item, button, or tool is used, Mesquite sends
a text command to the appropriate object, informing it of the
use. This means that most user interface commands can also be
executed in scripts and macros.
- When Mesquite re-reads a NEXUS file, it attempts to return
windows and analyses to approximately the same condition as
when the file had been saved. It does this by reading scripting
instructions in a MESQUITE block that Mesquite had written
into the file when saved. Thus, Mesquite writes scripts called
"Snapshots" to later instruct itself.
- Macros are script files which, if placed in the recognized
directories, appear in submenus as selectable menu items. When
selected, the macro is executed. Mesquite recognizes directories
called "macros" within the Mesquite_Folder directory
and the "Mesquite_Prefs" subdirectory of the Mesquite_Support_Files
directory.
- Not all scripts in MESQUITE blocks need to be written by Mesquite.
Users can write MESQUITE block scripts by hand.
- Users can send a script to a window using Window>Scripting>Send
Script. This page gives some simple
examples for the tree window.
The text commands are sent to modules or other objects. The particular
text commands to which a module or other object responds will
depend on the object. When Mesquite shows a web page for the first
time, it attempts to compile automatically documentation for scripting
commands that is available. This includes commands predefined
by the scripting language, and commands belonging to particular
modules. This compiled documentation is available from the web
page linked from the "Scripting Commands" menu
item in the Help menu when Mesquite is running.
Mesquite's scripting language is not as human-friendly as it
might be at present, especially in its handling of variables and
aspects such as the lack of "else" statements. Since
the vast majority of Mesquite scripts won't be written by humans
but by Mesquite itself, that is not such a liability as it might
seem. We hope to reform the scripting language in the future.
Macros
Scripts performing some special function can be written and distributed
by programmers to end users as "Macros". For some calculations
or display functions, the number of different analyses a user
might like to do are too many to be easily supported by a graphical
user interface. For instance, the user might like to print the
ancestral state reconstruction of a character using a series of
different stepmatrices. One can invent many such scenarios in
which something repetitious is needed, and a module would be hard-pressed
to maintain each as an option. Thus, small scripts using Mesquite's
scripting language can be written and placed in the "macros"
folder. These macros will appear as options in an appropriate
place, depending upon where they are applicable. When the user
selects the menu item, the script is executed.
Macros can also be automatically composed by Mesquite. As noted
above, Mesquite composes a script to place in NEXUS files that
it saves. This script applies only to the particular saved file.
In some contexts, you can ask Mesquite to save a script as a macro
file. This is currently available in only a few contexts. Save
Window as Macro in the Analysis menu and Window menu save macros
that attempt to reproduce the condition (including analyses) of
the foremost window. This is useful to reproduce a complex chart
with a different data file, for example. Save Macro For Tree Drawing
in the Drawing menu of a Tree Window creates a macro by which
you can later reproduce the current appearance of the tree drawing
(background color, font, tree form, orientation, and so on). Save
Tree Analysis As Macro in the Tree menu of a Tree window creates
a macro by which to reproduce a particular analysis with the tree.
Macros so created are stored in Mesquite_Support_Files/Mesquite_Prefs/macros,
and could be (for instance) shared by users.
Learning about scripting
Users can learn about the scripting language from this web page,
but also by inspection of existing scripts. Some sources of scripts
to examine are:
- The MESQUITE block within NEXUS files saved by Mesquite.
If you want to know how to script a tree window to trace a character,
for instance, open a tree window, turn on character tracing,
and save the file. You can look at the file with a text editing
program to see how such a script would be written
- If there are any macros installed, you can examine
the files that specify them. These files are simple text files
with scripts. Macros would be found in folders called "macros"
within the Mesquite_Folder directory, and in the macros folder
of the "Mesquite_Prefs" directory of the Mesquite_Support_Files
directory.
- The menu item Windows>Scripting>Show
Snapshot shows a snapshot for the window —
the script that would be needed to set the window to its current
state.
Another way to learn about the language and particular scripting
commands is to look at the web pages linked from the "Scripting
Commands" menu item in the Help menu when Mesquite is
running. In particular, examine the page on universal commands,
which shows basic commands available regardless of the object
being scripted, including the basic flow control and variable-defining
commands of the system. (We cannot provide a link here to this
web page because this web page is created by Mesquite when it
runs, and is placed in a location that depends on your particular
computer. After running Mesquite once, you may want to find some
of these files and save a bookmark/alias/shortcut to them —
the file on basic scripting commands is named 'puppeteer.html').
Commands within scripts
In general, a command found within a script takes the following
form:
commandName argument1 argument2 ...;
The commandName is a single word (token); the arguments can be
multiple tokens, so long as the module knows how to interpret
them. Typically each argument is a single token (though this may
be string of multiple tokens converted to a single token by quotation).
Scripts
A script consists of a series of commands. At each stage in the
script, there is an implicit recipient of the command given (for
instance, a module or a window). For instance, here is a script
within the MESQUITE block of a NEXUS file. To the right of the
commands are comments to explain them.
Begin MESQUITE;
getNumberOfDataSets; [file coordinator queried for number of data sets]
Integer.dataSets *it; [storing number of data sets in integer variable 'dataSets']
getEmployee 'Data Window Coordinator'; [querying for reference to Data Window Coordinator module]
tell It; [commands to follow will be sent to the Data Window Coordinator]
Integer.dataNum 0; [Define integer variable 'dataNum' and assign it 0]
for *Integer.dataSets; [for loop; cycle as many times as there are data sets]
showDataWindow *Integer.dataNum; [commands to make a data window for dataset]
tell It; [commands to follow will be sent to module that makes data window]
showWindow; [tells the module to make the data window visible]
endTell; [finished sending commands to the module that makes the data window]
increment.dataNum; [add 1 to the variable 'dataNum']
endFor; [end of the for loop]
endTell; [finished sending commands to the Data Window Coordinator]
getNumberOfTaxas; [file coordinator queried for number of sets of taxa]
Integer.taxaSets *it; [storing number of sets of taxa in integer variable 'taxaSets']
getEmployee 'Tree Window Coordinator'; [querying for reference to Tree Window Coordinator module]
tell It; [commands to follow will be sent to the Tree Window Coordinator]
Integer.taxaNum 0; [Define integer variable 'taxaNum' and assign it 0]
for *Integer.taxaSets; [for loop; cycle as many times as there are sets of taxa]
makeTreeWindow *Integer.taxaNum; [commands to make a tree window for set of taxa]
tell It; [commands to follow will be sent to module that makes tree window]
getTreeWindow; [queries the module to return a reference to the tree window itself]
tell It; [commands to follow will be sent to the tree window]
newAssistant 'Trace Character History'; [the tree window is asked to hire a module]
endTell; [finished sending commands to the tree window]
showWindow; [tells the module to make the tree window visible]
endTell; [finished sending commands to the module that makes the tree window]
increment.taxaNum; [add 1 to the variable 'taxaNum']
endFor; [end of the for loop]
endTell; [finished sending commands to the Tree Window Coordinator]
END;
This MESQUITE block causes Mesquite to show a data window for
each of the data sets, and a tree window for each of the Taxa
blocks; the tree windows are shown with a character traced.
This script illustrates some of the features of Mesquite's scripting
language:
- There is a current object to which commands are sent.
Which object is being commanded can be changed by a "tell"
command. Initially, the implicit recipient of the commands is
the FileCoordinator of the file in question.
- Any given command may return an object that is stored
within the variable referred to by "It". Thus,
in the above script, the makeTreeWindow returns the module hired
to supervise the tree window. The immediately following command,
"tell It" thus shifts the recipient of commands to
this tree window module.
- The scripting language has variables. They are defined
or their values set by commands beginning, for instance, Integer.myIntegerName
or Object.myObjectName. Their values can be utilized by prefixing
their names by an asterisk, for instance *Integer.myIntegerName.
- The scripting language has control flow, including
if, for loops, and while loops.
Some of the lines of this script are commands unique to particular
commandable objects (getNumberDataSets, getEmployee, showDataWindow,
etc.); others are predefined by the scripting language (e.g.,
Integer, tell, for). More details on particular commands can be
found in the web pages linked from the "Scripting Commands"
menu item in the Help menu when Mesquite is running. Some details
are found below.
Variables: Integers, Strings and Objects
Three sorts of variables are supported. Reference to each requires
the type of variable with name appended, as in "Integer.numberOfCharacters"
or "Object.treeDrawCoordinator". The generic variable
"it" refers to the object last returned by a command.
When the variable is passed as an argument for a command, it
should be preceeded by an asterisk. This allows the system
to know that a variable, and not a constant string, is being passeed.
Numerical variables
Two sorts of numerical variables are supported: Integer and Number.
The former contain whole numbers. The latter can contain whole
or decimal numbers. Reference to each requires the type of variable
with name appended, as in "Integer.numberOfCharacters"
or "Object.treeDrawCoordinator". The generic variable
"it" refers to the object last returned by a command.
When the variable is passed as an argument for a command, it
should be preceeded by an asterisk. This allows the system to
know that a variable, and not a constant string, is being passeed.
The commands concerning variables are:
- Integer.[name] [number]; this declares an integer
variable of name "name" and assigns it the value given
by the number. If the variable already exists, its value is
replaced by the number. The number may be represented by a constant,
as in "Integer.counter 5", by an integer variable,
as in "Integer.counter *Integer.previousCount", or
by a String variable that contains a string that can be parsed
into an integer, as in "Integer.counter *String.countString".
If the number is indicated as "random", a random integer
will be placed in the variable.
- increment.[name of integer]; this adds one to
the integer's value
- decrement.[name of integer]; this subtracts one
from the integer's value.
- Number.[name] [number]; this declares a numerical
variable of name "name" and assigns it the value given
by the number. If the variable already exists, its value is
replaced by the number. The number may be represented by a constant,
as in "Number.rate 0.5", by an integer variable, as
in "Number.rate *Number.previousRate", or by a String
variable that contains a string that can be parsed into a number,
as in "Number.rate *String.rateString". If the number
is indicated as "random", a random number between
0 and 1 will be placed in the variable. If the number to be
placed into the Number variable is preceeded by a '+', the number
doesn't replace the existing value of the Number variable, but
is added to it (similarly for '-').
String variables
One sort of variable contains a string of text. The command to
define and assign values to a string variable is:
- String.[name] [string]; this declares a String
variable of name "name" and assigns it the value given
by the string. If the variable already exists, its value is
replaced by the string, unless the string passed to it is preceded
by "+" in which case it is appended to the existing
string. The string passed may be represented by a literal string,
as in "String.name John A. MacDonald", by an String
variable, as in "String.name *String.name.firstPM",
or by an Object variable, in which case the name of the Object
will be used.
Object variables
One sort of variable contains a objects (such as modules, or
windows). The command to define and assign values to an object
variable is:
- Object.[name] [reference to object]; this declares
an Object variable and assigns it the object indicated. If the
variable already exists, its value is replaced. The reference
may be "it", as in "Object.thisModule *It",
or an Object variable, as in "Object.thisModule *Object.storedModule".
The variable "It"
Standard Mesquite Commands to modules return an object. In the
scripting language, this returned object is stored in the variable
"It". Thus, after a command "getNumberDataSets"
to the FileCoordinator, the FileCoordinator returns an Integer
variable containing the number of data sets. This can be stored
in an Integer variable by following the command by "Integer.numDataSets
*it". Likewise, "tell" often makes use of "it".
Flow and command control
As noted above Mesquite's scripting language has flow control
as well as control of the object to be commanded.
Using "tell" to direct commands
Commands are directed toward commandable objects, including modules,
windows, and others. Since different objects might use the same
command names, the object to which a command is directed must
be indicated. In the scripting language, at any point there is
an implicit object being commanded. Subsequent commands are directed
to a different object using the "tell" command, which
must be balanced by "endTell". At the root level, the
FileCoordinator is being commanded.
Flow control
Flow control statements include "if", "for",
and "while". Others are available (such as ifnot,
stop, exitTell). Details on these can be found via
the web page shown by selecting Scripting Commands from
the Help menu while Mesquite is running.
- if [integer or integer variable]; ... endIf;
the statements between if and endIf are executed if the
integer variable is non-zero
- for [integer or integer variable]; ... endFor;
the statements between for and endFor are executed as
many times as the initial value of the integer variable.
- while [integer or integer variable]; ... endWhile;
the statements between while and endWhile are executed
repeatedly as long as the value of the integer variable is non-zero.
Debugging
There are a number of commands that are useful for debugging.
For instance, if the Command "debug" is placed in the
block, a debugging mode will be enabled which reports in the console
more details about the commands as they are executed. More information
about such commands can be found by selecting the "Scripting
Commands" menu item under the Help menu when Mesquite
is running.
Examples
Here are three simple example scripts that you can send to the
Tree window using Window>Scripting>Send Script. Open
a tree window, turn on Trace Character History, and then paste
one of these scripts into the Send Script dialog box. The
first script scrolls from tree to tree, for each recording in
a file "results.txt" the reconstruction of ancestral
states.
String.resultsFile 'results.txt';
saveMessageToFile *String.resultsFile 'RESULTS with different trees';
appendReturnToFile *String.resultsFile;
getWindow;
tell It;
getNumTrees;
Integer.numReps *It;
ifNotCombinable *Integer.numReps;
Integer.numReps 10; [in case indefinite number of trees]
endIf;
endTell;
Integer.count 0;
for *Integer.numReps;
increment.count;
getWindow;
tell It;
setTreeNumber *Integer.count;
endTell;
getEmployee #mesquite.ancstates.RecAncestralStates.RecAncestralStates;
tell It;
getLastResult;
String.result *It;
appendMessageToFile *String.resultsFile *String.result;
appendReturnToFile *String.resultsFile;
endTell;
endFor;
This second script scrolls from character to character,
for each recording the ancestral states in a file "results.txt":
String.resultsFile 'results.txt';
saveMessageToFile *String.resultsFile 'RESULTS with different characters';
appendReturnToFile *String.resultsFile;
getEmployee #mesquite.ancstates.RecAncestralStates.RecAncestralStates;
tell It;
getNumHistories;
Integer.numReps *It;
ifNotCombinable *Integer.numReps;
Integer.numReps 10; [in case indefinite number of characters]
endIf;
endTell;
Integer.count 0;
for *Integer.numReps;
increment.count;
getEmployee #mesquite.ancstates.TraceCharacterHistory.TraceCharacterHistory;
tell It;
setCharacter *Integer.count;
endTell;
getEmployee #mesquite.ancstates.RecAncestralStates.RecAncestralStates;
tell It;
getLastResult;
String.result *It;
appendMessageToFile *String.resultsFile *String.result;
appendReturnToFile *String.resultsFile;
endTell;
endFor;
The third script scrolls from tree to tree, printing
each one.
getWindow;
tell It;
getNumTrees;
Integer.numReps *It;
ifNotCombinable *Integer.numReps;
Integer.numReps 10; [in case indefinite number of trees]
endIf;
endTell;
for *Integer.numReps;
getWindow;
tell It;
setTreeNumber *Integer.count;
printToFit;
endTell;
endFor;
Technical Details
(See the developer's
documentation.) The Puppeteer is in charge of defining the
basic language as it relates to variables and flow control. For
an object to be scriptable, it must be of the Commandable interface.
Commands internally in Mesquite are remembered in objects called
MesquiteCommands.