Macro Language Reference |
Data Types Boxer's Macro Language supports the following data types:
string char int float
A string can hold a series of characters up to 2,048 bytes in length. The end of a string is marked with a Null character (ASCII 0). A string constant is enclosed within double quotes.
The char data type is an 8-bit, unsigned data type which can hold values in the range 0 to 255. A character constant is enclosed within single quotes.
The int data type is a 32-bit, signed data type which can hold integer values in the range -2,147,483,648 to 2,147,483,647.
The float data type is a double precision, signed data type that can hold values in the range 2.2250738585072014e-308 to 1.7976931348623158e+308.
Keywords The following words are reserved keywords and may not be used as variable names:
break int true continue char false do string yes else float no for void on goto off if macro return while
Arithmetic Operators The following arithmetic operators are supported:
The modulus operator (%) returns the remainder from an integer division operation. For example, the expression n = 7 % 4 will result in n receiving the value 3, since 7 / 4 leaves a remainder of 3.
The increment and decrement operators can be used to increase or decrease an integer variable by 1. The expression:
i++;
is equivalent to:
i = i + 1;
The ++ and -- operators can be used in either prefix or postfix location. If i has an initial value of 3, the statement:
n = i++;
will leave n with the value of 3, while i is incremented to 4. The increment occurs after the assignment due to the postfix location.
Assuming i again starts with a value of 3, the statement:
n = --i;
will leave n with a value of 2 and i with a value of 2. The decrement occurs before the assignment due to the prefix location.
The addition (+) operator has been overloaded to support string concatenation. The following statements:
string s1 = "Boxer "; string s2 = "Text Editor"; string s3 = s1 + s2;
would result in s3 having the value: "Boxer Text Editor"
Assignment Operators The following assignment operators are supported:
The assignment operator (=) should be familiar to all. The other operators which each conclude with = all represent a shorthand notation. For example, the statement:
i += 5;
is equivalent to:
i = i + 5;
The += operator has been overloaded to support string concatenation. The following statements:
string str = "Boxer "; str += "Text Editor";
would result in str having the value: "Boxer Text Editor"
The last five operators listed above are bitwise assignment operators. Their function is analogous to the += operator; see the Bitwise Operators section of this topic for some additional detail.
Boolean Operators The following Boolean operators are supported:
The operators ==, !=, <, >, <= and >= have been overloaded to allow operations on strings. A string is considered greater than another string if it would appear higher in an alphabetic sort. In other words, the statement:
if ("apple" < "zebra")
evaluates to TRUE.
The first nine operators above are standard to most high-level languages. The last operator is specific to Boxer's Macro Language, and permits strings to be compared without case sensitivity. For example, the statement:
if ("MasterCard" ~= "mastercard")
would evaluate to TRUE.
Bitwise Operators The following bitwise operators are supported:
A full discussion of bitwise arithmetic would be beyond the scope of this language reference. For those who are interested, any introductory book on the C programming language would be a suitable reference. The information below will be sufficient to remind those with prior experience of the function of each operator:
& Sets a bit to 1 in the result if and only if both of the corresponding bits in its operands are 1, and to 0 if the bits differ or both are 0. Example: 9 & 1 yields 1.
| Sets a bit to 1 in the result if one or both of the corresponding bits in its operands are 1, and to 0 if both of the corresponding bits are 0. Example: 9 | 2 yields 11.
^ Sets a bit in the result to 1 when the corresponding bits in its operands are different, and to 0 when they are the same. Example: 7 ^ 4 yields 3;
<< Shifts the first operand the number of bits to the left specified in the second operand, filling with zeros from the right. Example: 2 << 3 yields 16.
>> Shifts the first operand the number of bits to the right specified in the second operand, discarding the bits that 'fall off' at the right. Example: 34 >> 2 yields 8.
~ Inverts each bit in the operand, changing all ones to zeros and all zeros to ones. Example: ~0xFFFF0000 yields 0x0000FFFF.
Operator Precedence The following table summarizes operator precedence and order of evaluation for the various operators supported by Boxer's Macro Language. Operators with the strongest/highest precedence are listed first:
Parentheses can be used when required to ensure that the order of evaluation occurs as desired. For example:
n1 = 3 * 5 + 4;
assigns 19 to n1, while:
n1 = 3 * (5 + 4);
assigns 27 to n1.
int i, j, k; i = j = k = 0;
k is assigned the value 0, j is assigned the value of k, and i is assigned the value of j.
Character Constants Boxer's Macro Language recognizes the standard character constants which have been popularized by the C programming language:
In addition, Boxer will recognize a backslash (\) followed by three octal digits as the character whose ASCII value is given by the digits used. For example, '\101' could be used to represent a capital A, since its ASCII value, in octal, is 101.
Character constants can be used in any place that a char data type is expected, or within a double-quoted string: "this is a string with a newline at the end.\n"
Numeric Constants Numeric int constants can be specified in either decimal or hexadecimal format:
int n1 = 32; int n2 = 0x20;
Each of these assignments supplies the value 32 to n1 or n2.
Numeric float constants can be specified in any of the following forms:
float x1 = 500; float x2 = 500.0; float x3 = 5e2; float x4 = 5e02; float x5 = 5.0e2; float x6 = 5.0e02; float x7 = 5.0e+2; float x7 = 5.0e+02;
Each of these assignments results in the value 500 being assigned to the variable being declared.
For floating point values less than 1, the minus sign can be used to designate exponentiation. All of the following examples represent the number .05:
.05 0.05 5e-2 5e-02 5.0e-2 5.0e-02
Symbolic Constants The following symbolic constants are recognized:
These constants can be used in place of the values 0 and 1 to make a macro more readable. For example, you can write:
ViewBookmarks(ON);
instead of:
ViewBookmarks(1);
Declaring Variables Variable names can be up to 32 characters in length and must not conflict with the names of any keywords or internal functions. Variable names can use alphanumeric characters and the underscore ( _ ), but they must not start with a digit. All variables must be declared before use. Initialization of variables can be done at declaration-time, but this is not required. Uninitialized variables will be zero-filled automatically.
Boxer's Macro Language supports a flexible syntax for declaring variables. All of the following examples are legal declarations when they appear at the top of a macro, before other executable statements:
string s1; string s2 = "Boxer"; string s3, s4, s5; string s6 = "abc", s7, s8 = "def";
char c1; char c2 = 'A'; char c3, c4, c5; char c6, c7 = 'x', c8;
int n1; int n2 = 10; int n3, n4, n5; int n6, n7 = -4, n8;
float x1; float x2 = 1.05; float x3 = 1.2e04; float x4, x5, x6; float x7, x8 = 7.75, x9;
In the spirit of the C programming language, Boxer's macro language also allows a string variable to be declared as an array of characters. The declaration:
char str[100];
is (for most purposes) functionally equivalent to the declaration:
string str;
for declaring a variable which can hold a short string of characters. See the String Subscripting section below for details on when the former style might be required.
Conditional Statements Boxer's Macro Language supports three different conditional statements: if, if-else and the ternary statement. Below are examples of each of these statements:
if (LineCount() > 10000) { longfile = true; }
if (LineCount() > 10000) { longfile = true; } else { longfile = false; }
longfile = (LineCount() > 10000) ? true : false;
In the first example, the variable longfile is set TRUE if the return from the function LineCount() is greater than 10000. In the second example, an if-else statement is used to additionally set longfile to FALSE if the condition is not met.
The final example illustrates the ternary statement, and its effect is identical to the if-else example immediately above it. If the condition within parentheses evaluates to TRUE, the expression immediately following the ? is evaluated. If not, the expression after the : is evaluated. A ternary statement is effectively a compact if-else statement.
Looping Statements Boxer's Macro Language supports three different looping statements: for, while and do-while. Below are examples of each of these statements:
// find the longest line in the file for (line = 1, longest = 0; line <= LineCount(); line++) if ((n = LineLength(line)) > longest) longest = n;
// find the longest line in the file line = 1; longest = 0; while (line <= LineCount()) { if ((n = LineLength(line)) > longest) longest = n;
line++; }
// find the longest line in the file line = 1; longest = 0; do { if ((n = LineLength(line)) > longest) longest = n;
line++; } while (line < LineCount());
The three loops above are functionally equivalent to one another, with one exception that will be discussed below.
The for loop is the most compact, since it permits the three elements of a loop's control to be specified on a single line: the initialization, the test, and the increment. These are found within the parentheses of the for loop and are separated by semi-colons. When a for loop is first executed, the initialization section is performed, and the test section is evaluated. If the test evaluates to TRUE, the statement(s) in the body of the loop are processed. At the end of the loop, the increment section is processed. Control then passes again to to the test section, to the body, and so on.
The while loop is a simpler loop, in that the only required control element that must be supplied is the test. For illustration purposes, the while loop above was written to be identical in function to the for loop above it. In fact, every for loop can be written as a while loop, and every while loop can be written as a for loop. A for loop is typically used when one needs to initialize and increment a loop index. A while loop is typically used when a single condition is sufficient to control the flow of the loop.
A do-while loop is essentially an upside-down while loop. A do-while loop tests at the bottom, whereas a while loop tests at the top. A do-while loop should be used in those cases where the loop always wants to be executed at least once. That leads us to why the do-while example above is not exactly equivalent to the for and while loops above it. If the current file is empty, the for and while loops above will not be executed. The LineCount() function will return 0 and the initial test will fail. In the do-while loop, the LineCount() call isn't made until the bottom of the loop. In the case of an empty file, the body of the loop would be processed and the LineLength() call would fail because the line parameter would be out of range.
// loop until the user enters the right answer for (;;) { GetString("What's the capital of Arizona?", answer);
if (answer ~= "Phoenix") break; }
// loop until the user enters the right answer while (TRUE) { GetString("What's the capital of New Hampshire?", answer);
if (answer ~= "Concord") break; }
Alert readers might notice that the above examples could be more neatly implemented using a do-while loop, since this is a case where the loop always wants to be run once, and the test can be more logically placed at the bottom of the loop:
do GetString("What's the capital of California?", answer); while (strcmpi(answer, "Sacramento") != 0);
The break Statement The break statement can be used to exit from a loop prematurely. Control passes to the next statement following the loop which has been exited. For example:
// loop on all lines in the file for (i = 1; i <= LineCount(); i++) { // exit the loop if a line is longer than 1000 characters if (LineLength(i) > 1000) break; }
// control passes to here after break New;
The continue Statement The continue statement can be used to jump to the bottom of a loop prematurely. Control passes to an imaginary label at the end of the loop. For example:
// loop on all lines in the file for (i = 1; i <= LineCount(); i++) { // exit the loop if a line is longer than 1000 characters if (LineLength(i) > 1000) continue;
// ... other processing ...
// continue jumps to here }
The goto Statement The goto statement can be used to jump unconditionally to a label. Control passes to the next statement after the label. For example:
// loop on all lines in the file for (i = 1; i <= LineCount(); i++) { // exit the loop if a line is longer than 1000 characters if (LineLength(i) > 1000) goto toolong;
// ... other processing ...
toolong: // goto jumps to here
// ... other processing ... }
The return Statement The return statement can be used to end a macro prematurely. If a return statement is not encountered, a macro will run until the closing curly brace in the body of the macro is encountered.
Function Calls Boxer's Macro Language includes a wide variety of functions that provide access to the editor's commands, configuration settings, and to string and math libraries. The function set is documented in the Macro Function Reference, as well as in the Macro Dialog itself.
When making a function call, care should be taken to ensure that the parameters supplied to the function match the declared type(s) that the function expects to receive. Boxer is able to trap missing and/or mismatched parameters in most cases, but unexpected results can occur when invalid parameters are supplied.
Function names are not case sensitive; Boxer will accept function names that do not match the function name with regard to character case.
If a function does not require parameters, it is not necessary to supply parentheses at the end of the function name. For example:
LineCount();
and
LineCount;
are functionally equivalent, because the LineCount function does not require any parameters. That said, the practice of using () on all function calls can help to distinguish function names from variable names.
Simple expressions can be supplied to in a function call without difficulty, and they will be evaluated as expected before being sent to the function for processing. For example:
max(3 * 45, 4 * 90);
is a legitimate construction that might be used in calling the max() function. If you find that you are getting unexpected results in a case like this, introduce a temporary variable to hold the value of the expression, and then supply the variable to the function in place of the expression.
String Subscripting Arrays are not supported in the classical sense; it's not possible to declare an array of int or float variables, for example. But Boxer's Macro Language does recognize a string variable to be an array of elements of type char, and allows those elements to be accessed individually through the use of subscripts. The first character within a string is located at index 0, the second character is at index 1, etc. In the following example:
string str = "BOXER"; char c1; c1 = str[2];
the character variable c1 would be assigned the value 'X'.
Likewise, a string variable can be modified by assigning individual elements within the string using subscripting:
string s1 = "water"; s1[0] = 'w'; s1[1] = 'i'; s1[2] = 'n'; s1[3] = 'e'; s1[4] = '\0';
This code fragment has the effect of changing the content of string variable s1 from "water" to "wine". Notice that the null character ('\0') was used to shorten the string from five characters to four.
macro array_example() { int i; string input = "now is the time for all good men to come to the aid of their country."; char tally[256]; // note that all elements are initially set to zero
// loop to process all characters in the input string for (i = 0; input[i] != '\0'; i++) tally[input[i]]++;
// open a new, untitled file New;
// report the results for lowercase letters for (i = 'a'; i <= 'z'; i++) printf("letter %c occurred %d time(s)\n", i, tally[i]); }
Had the tally array been declared as a string type, Boxer's built-in range checking would have prevented the string from being used in the way that was shown above. By declaring the string as a character array of sufficient size, the macro processor is forewarned that the code may later index into the string beyond the terminating null character.
Type Conversions Boxer's Macro Language will automatically convert between data types whenever possible in order to resolve an expression that involves mismatched data types. Here are some examples:
string s1 = 'A'; // result: s1 gets "A" (char to string) string s2 = 65; // result: s2 gets "A" (int to string) string s3 = 65.0; // result: s3 gets "A" (float to string)
char c1 = 65; // result: c1 gets 'A' (int to char) char c2 = "A"; // result: c2 gets 'A' (string to char) char c3 = 65.0; // result: c3 gets 'A' (float to char)
int n1 = 'A'; // result: n1 gets 65; (char to int) int n2 = "123"; // result: n2 gets 123 (string to int) int n3 = 123.45; // result: n3 gets 123 (float to int)
float x1 = 'A' // result: x1 gets 65.0 (char to float) float x2 = 65; // result: x2 gets 65.0 (int to float) float x3 = "123.45"; // result: x3 gets 123.45 (string to float)
Comments Comments can be placed throughout a macro to help document the code. Two types of comments are supported, block comments and end-of-line comments:
/* this is a multi-line block comment */
int n1 = 7; // this is an end-of-line comment
|