Macro Language

Macros can be called from Macro menu commands, window background menu commands, within the smart-indent framework, from the autoload macro file, cf. Preferences, and from the command line. Macro menu and window background menu commands are defined under Preferences -> Default Settings -> Customize Menus. Help on creating items in these menus can be found in the section Preferences.

XNEdit's macro language is a simple interpreter with integer arithmetic, dynamic strings, and C-style looping constructs (very similar to the procedural portion of the Unix awk program). From the macro language, you can call the same action routines which are bound to keyboard keys and menu items, as well additional subroutines for accessing and manipulating editor data, which are specific to the macro language (these are listed in the sections titled "Macro Subroutines", and "Action Routines").

Syntax

An XNEdit macro language program consists of a list of statements, each terminated by a newline. Groups of statements which are executed together conditionally, such as the body of a loop, are surrounded by curly braces "{}".

Blank lines and comments are also allowed. Comments begin with a "#" and end with a newline, and can appear either on a line by themselves, or at the end of a statement.

Statements which are too long to fit on a single line may be split across several lines, by placing a backslash "\" character at the end of each line to be continued.

Data Types

The XNEdit macro language recognizes only three data types, dynamic character strings, integer values and associative arrays. In general strings and integers can be used interchangeably. If a string represents an integer value, it can be used as an integer. Integers can be compared and concatenated with strings. Arrays may contain integers, strings, or arrays. Arrays are stored key/value pairs. Keys are always stored as strings.

Integer Constants

Integers are non-fractional numbers in the range of -2147483647 to 2147483647. Integer constants must be in decimal. For example:

    a = -1
    b = 1000

Character String Constants

Character string constants are enclosed in double quotes. For example:

     a = "a string"
     dialog("Hi there!", "OK")

Strings may also include C-language style escape sequences:

     \\ Backslash     \t Tab              \f Form feed
     \" Double quote  \b Backspace        \a Alert
     \n Newline       \r Carriage return  \v Vertical tab

Also allowed is the escape control character sequence:

     \e Escape (ASCII or EBCDIC,
                depending on XNEdit compilation settings)

For example, to send output to the terminal from which XNEdit was started, a newline character is necessary because, like printf, t_print requires explicit newlines, and also buffers its output on a per-line basis:

     t_print("a = " a "\n")

Other characters can be expressed as backslash-escape sequences in macro strings. The format is the same as for regular expressions, described in the paragraphs headed "Octal and Hex Escape Sequences" of the section "Metacharacters", except that an octal escape sequence can start with any octal digit, not just 0, so the single character string "\0033" is the same as "\33", "\x1B" and "\e" (for an ASCII version of XNEdit).

Note that if you want to define a regular expression in a macro string, you need to "double-up" the backslashes for the metacharacters with special meaning in regular expressions. For example, the expression

     (?N(\s|/\*(?n(?:(?!\*/).)*)\*/|//.*\n|\n)+)

which matches whitespace or C/C++/Java-style comments, should be written as a macro string as

     "(?N(\\s|/\\*(?n(?:(?!\\*/).)*)\\*/|//.*\n|\n)+)"

(The "\n"s towards the end add literal newline characters to the string. The regular expression interpretation treats the newlines as themselves. It can also interpret the sequence "\\n" as a newline, although the macro string here would then contain a literal backslash followed by a lowercase `N'.)

Variables

Variable names must begin either with a letter (local variables), or a $ (global variables). Beyond the first character, variables may also contain numbers and underscores `_'. Variables are called in to existence just by setting them (no explicit declarations are necessary).

Local variables are limited in scope to the subroutine (or menu item definition) in which they appear. Global variables are accessible from all routines, and their values persist beyond the call which created them, until reset.

Built-in Variables

XNEdit has a number of permanently defined variables, which are used to access global editor information and information about the window in which the macro is executing. These are listed along with the built in functions in the section titled "Macro Subroutines".

Functions and Subroutines

The syntax of a function or subroutine call is:

     function_name(arg1, arg2, ...)

where arg1, arg2, etc. represent the argument values which are passed to the routine being called. A function or subroutine call can be on a line by itself, as above, or if it returns a value, can be invoked within a character or numeric expression:

     a = fn1(b, c) + fn2(d)
     dialog("fn3 says: " fn3())

Arguments are passed by value. This means that you cannot return values via the argument list, only through the function value or indirectly through agreed-upon global variables.

Built-in Functions

XNEdit has a wide range of built in functions which can be called from the macro language. These routines are divided into two classes, macro-language functions, and editor action routines. Editor action routines are more flexible, in that they may be called either from the macro language, or bound directly to keys via translation tables. They are also limited, however, in that they cannot return values. Macro language routines can return values, but cannot be bound to keys in translation tables.

Nearly all of the built-in subroutines operate on an implied window, which is initially the window from which the macro was started. To manipulate the contents of other windows, use the focus_window subroutine to change the focus to the ones you wish to modify. focus_window can also be used to iterate over all of the currently open windows, using the special keyword names, "last" and "next".

For backwards compatibility, hyphenated action routine names are allowed, and most of the existing action routines names which contain underscores have an equivalent version containing hyphens ('-') instead of underscores. Use of these names is discouraged. The macro parser resolves the ambiguity between '-' as the subtraction/negation operator, and - as part of an action routine name by assuming subtraction unless the symbol specifically matches an action routine name.

User Defined Functions

Users can define their own macro subroutines, using the define keyword:

     define subroutine_name {
        < body of subroutine >
     }

Subroutine definitions cannot appear within other definitions, nor within macro menu item definitions. They can only appear in (macro) files, such as the autoload macro file, cf. Preferences. Macro files can be loaded with File -> Load Macro File or with the load_macro_file() action.

The arguments with which a user-defined subroutine or function was invoked, are presented as $1, $2, ... , $9 or $args[expr], where expr can be evaluated to an integer from 1 to the number of arguments. The number of arguments can be read from $n_args or $args[]. The array $args[expr] is the only way to access arguments beyond the first 9.

To return a value from a subroutine, and/or to exit from the subroutine before the end of the subroutine body, use the return statement:

     return <value to return>

Operators and Expressions

Operators have the same meaning and precedence that they do in C, except for ^, which raises a number to a power (y^x means y to the x power), rather than bitwise exclusive OR. The table below lists operators in decreasing order of precedence.

     Operators                Associativity
     ()
     ^                        right to left
     - ! ++ --                (unary)
     * / %                    left to right
     + -                      left to right
     > >= < <= == !=          left to right
     &                        left to right
     |                        left to right
     &&                       left to right
     ||                       left to right
     (concatenation)          left to right
     = += -= *= /= %=, &= |=  right to left

The order in which operands are evaluated in an expression is undefined, except for && and ||, which like C, evaluate operands left to right, but stop when further evaluation would no longer change the result.

Numerical Operators

The numeric operators supported by the XNEdit macro language are listed below:

     + addition
     - subtraction or negation
     * multiplication
     / division
     % modulo
     ^ power
     & bitwise and
     | bitwise or

Increment (++) and decrement (--) operators can also be appended or prepended to variables within an expression. Prepended increment/decrement operators act before the variable is evaluated. Appended increment/decrement operators act after the variable is evaluated.

Logical and Comparison Operators

Logical operations produce a result of 0 (for false) or 1 (for true). In a logical operation, any non-zero value is recognized to mean true. The logical and comparison operators allowed in the XNEdit macro language are listed below:

     && logical and
     || logical or
     !  not
     >  greater
     <  less
     >= greater or equal
     <= less or equal
     == equal (integers and/or strings)
     != not equal (integers and/or strings)

Character String Operators

The "operator" for concatenating two strings is the absence of an operator. Adjoining character strings with no operator in between means concatenation:

     d = a b "string" c
     t_print("the value of a is: " a)

Comparison between character strings is done with the == and != operators, (as with integers). There are a number of useful built-in routines for working with character strings, which are listed in the section called "Macro Subroutines".

Arrays and Array Operators

Arrays may contain either strings, integers, or other arrays. Arrays are associative, which means that they relate two pieces of information, the key and the value. The key is always a string; if you use an integer it is converted to a string.

To determine if a given key is in an array, use the 'in' keyword.

      if ("6" in x)
          <body>

If the left side of the in keyword is an array, the result is true if every key in the left array is in the right array. Array values are not compared.

To iterate through all the keys of an array use the 'for' looping construct. Keys are not guaranteed in any particular order:

      for (aKey in x)
          <body>

Elements can be removed from an array using the delete command:

      delete x[3] # deletes element with key 3
      delete x[]  # deletes all elements

The number of elements in an array can be determined by referencing the array with no indices:

      dialog("array x has " x[] " elements", "OK")

Arrays can be combined with some operators. All the following operators only compare the keys of the arrays.

      result = x + y   (Merge arrays)

The 'result' is a new array containing keys from both x and y. If duplicates are present values from y are used.

      result = x - y   (Remove keys)

The 'result' is a new array containing all keys from x that are not in y.

      result = x & y   (Common keys)

The 'result' is a new array containing all keys which are in both x and y. The values from y are used.

      result = x | y   (Unique keys)

The 'result' is a new array containing keys which exist in either x or y, but not both.

When duplicate keys are encountered using the + and & operators, the values from the array on the right side of the operators are used for the result. All of the above operators are array only, meaning both the left and right sides of the operator must be arrays. The results are also arrays.

Array keys can also contain multiple dimensions:

      x[1, 1, 1] = "string"

These are used in the expected way, e.g.:

      for (i = 1; i < 3; i++)
      {
          for (j = 1; j < 3; j++)
          {
              x[i, j] = k++
          }
      }

gives the following array:

      x[1, 1] = 0
      x[1, 2] = 1
      x[2, 1] = 2
      x[2, 2] = 3

Internally all indices are part of one string, separated by the string $sub_sep (ASCII 0x1c, 'FS'). The first key in the above example is in fact:

      ["1" $sub_sep "1"]

If you need to extract one of the keys, you can use split(), using $sub_sep as the separator.

You can also check for the existence of multi-dimensional array by looking for $sub_sep in the key.

Last, you need $sub_sep if you want to use the 'in' keyword.

      if ((1,2) in myArray)
      {..}

doesn't work, but

      if (("1" $sub_sep "2") in myArray)
      {..}

does work.

Looping and Conditionals

XNEdit supports looping constructs: for and while, and conditional statements: if and else, with essentially the same syntax as C:

     for (<init>, ...; <condition>; <increment>, ...) <body>

     while (<condition>) <body>

     if (<condition>) <body>

     if (<condition>) <body> else <body>

<body>, as in C, can be a single statement, or a list of statements enclosed in curly braces ({}). <condition> is an expression which must evaluate to true for the statements in <body> to be executed. for loops may also contain initialization statements, <init>, executed once at the beginning of the loop, and increment/decrement statements (or any arbitrary statement), which are executed at the end of the loop, before the condition is evaluated again.

Examples:

    for (i=0; i<100; i++)
       j = i * 2

    for (i=0, j=20; i<20; i++, j--) {
       k = i * j
       t_print(i, j, k)
    }

    while (k > 0)
    {
       k = k - 1
       t_print(k)
    }

    for (;;) {
       if (i-- < 1)
           break
    }

Loops may contain break and continue statements. A break statement causes an exit from the innermost loop, a continue statement transfers control to the end of the loop.