Macro Language Reference

Contents  Previous  Next

 

Data Types

Boxer's Macro Language supports the following data types:

 

       string

       char

       int

       float

 

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:

 

Operator

Meaning

+

addition

-

subtraction

*

multiplication

/

division

%

modulus

++

increment

--

decrement

 

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:

 

Operator

Meaning

=

assignment

+=

addition assignment

-=

subtraction assignment

*=

multiplication assignment

/=

division assignment

%=

modulus assignment

&=

bitwise AND assignment

|=

bitwise OR assignment

^=

bitwise XOR assignment

<<=

left shift assignment

>>=

right shift assignment

 

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:

 

Operator

Meaning

==

equal

!=

not equal

<

less than

>

greater than

<=

less than or equal to

>=

greater than or equal to

&&

logical AND

||

logical OR

!

logical NOT (unary negation)

~=

case insensitive string comparison

 

 

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:

 

Operator

Meaning

&

bitwise AND

|

bitwise OR

^

bitwise XOR

<<

left shift

>>

right shift

~

one's complement (unary)

 

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.

 

bm2The large majority of users will never find a need for bitwise arithmetic, but it has been included in the interest of completeness.

 

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:

 

Operator

Evaluates

(  )  [  ]

left to right

!  ~  ++  --  -

right to left

*  /  %

left to right

+  -

left to right

<<  >>

left to right

<  <=  >  >=

left to right

==  !=  ~=

left to right

|

left to right

&

left to right

^

left to right

&&

left to right

||

left to right

?  :

right to left

=  +=  -=  etc.

right to left

,

left to right

 

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.

 

bm1Because the assignment operator (=) is evaluated from right to left, a construction such as the following is possible:

 

       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:

 

 

Sequence

Meaning

Decimal Value

'\b'

Backspace

8

'\f'

Formfeed

12

'\n'

Newline

10

'\r'

Carriage Return

13

'\t'

Tab

9

'\\'

Backslash

92

'\''

Single Quote

39

'\"'

Double Quote

34

'\0'

Null

0

 

 

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:

 

Name

Value

TRUE

1

FALSE

0

YES

1

NO

0

ON

1

OFF

0

 

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.

 

bm2The ternary statement in Boxer's Macro Language is modeled after that of the C programming language, with one exception.  In Boxer macros, the parentheses around the conditional expression are required, in C these parentheses are optional.

 

bm2When a single statement is conditional upon an if or if-else statement, as is shown in the examples above, the use of curly braces {­ } is not required.  Curly braces are required when two or more statements are to be conditionally executed, or when those statements are the subject of a looping statement.

 

Looping Statements

Boxer's Macro Language supports three different looping statements: forwhile 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.

 

bm2Boxer's Macro Language supports a very flexible  for loop structure.  The initialization, test and increment sections are each optional.  Moreover, multiple initializations can be performed by separating the statements with the comma operator.

 

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.

 

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.

 

bm2Sometimes the need arises to construct a 'forever' loop; one which will run until some condition within the body of the loop is satisfied and a break statement is executed.  Both the  for and  while loops can be used for this purpose.  Here are two examples:

 

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

               }

 

bm1Notice that these examples used the  ~= operator to ensure that the user's response was not rejected due to improper case.

 

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

 

bm2This example uses the  strcmpi() function to perform a case insensitive string comparison, because the  ~= operator does not have a companion string-does-not-match operator.

 

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.

 

bm1String subscripting makes it possible to use a string variable in the way that an array might be used.  Here's an example that totals the number of occurrences of each letter within an input string:

 

       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.

 

bm2Due to the capacity of the char data type (0-255), the utility of the above technique is limited to applications in which the maximum number of occurrences would be less than 256.

 

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