WinPTE.com

 

WinPTE Macro Language

Macros

First a comment on how the language evolved. It started out as a simple set of primitive functions to move cursor [up], [down], [left], [right], etc. As the need grew I kept adding more functionality. If conditionals, looping constructs, equivalent of variables, expression evaluation, etc. At each step the decision to 'add' into the existing structure vs. creating a whole new language came up but was always shut down simply because I needed the functionality now and did not have the luxury of waiting for a rewrite.

Keep in mind that for nearly 15 years WinPTE was strictly an indispensable tool in my arsenal and not a commercial effort. So as far as a rewrite was concerned, I could not justify the effort for a user base of one. Over the years some of these decisions accumulated to a point where I wound up rewriting most of the core over the last year and a half. There are only a couple of places left that still have the "old code", the built-in macros are one of them. Now I am not about to write a new script language when many mature, complete and open source efforts are available. But the job of replacing the macro language has to include the rewrite of about 2.5MB of macro source in 100 files (I have more than is included in the standard distribution). Is it worth it? Yes. But it will make WinPTE un releasable until the job is complete and tested so I am waiting to get to a version that is "feature complete" enough to wait a month or two for the macro language rewrite release.

That said here is the reference:

A macro definition consists of a sequence of macro functions enclosed in [ ] and/or string constants enclosed in ' '. An asterisk starts a one line comment, multi line comments are the same as C/C++ multi-line comments, start with /* and close with */.

Some functions can take an optional numeric parameter and it is added after the function name and before the closing square bracket. Functions that can take a numeric argument will be provided with a default value of 1 if no argument is given. Therefore [up] and [up 1] are equivalent and will move the cursor up by one line. Additionally the numeric argument can be passed in the string immediately following the function. For this to happen the numeric parameter should be replaced with #. So [up #]'1' is equivalent to the preceding two examples. Constant Numeric parameter has a valid range of 1..254. Larger values can be passed using the # string parameter format.

Functions may also require one or more string parameters and these are taken from the strings that follow the function. Some functions return a string result and these can be used in places where a string parameter is required. All string constants not used as parameters by a function are inserted at the current cursor position. So a macro definition that consists of 'string' will have the effect of inserting 'string' at the cursor position when it is executed. Since there is no way to specify that the string constant is to be used as an argument to a preceding function, any extra ones will wind up as text inserted into the file and any missing will be provided as empty strings.

There is a single string concatenation operator &.and will concatenate the string to its left with the string to its right. Because there are no parentheses in the language to control evaluation order it is sometimes necessary to concatenate the string result returned by a function using the [concatenate] function which takes two string arguments that immediately follow it and returns a concatenated result.

Functions will raise an error on failure and if this error is not tested with a preceding [if] or [if not] function then the error will terminate execution of all macros and display an error in the status bar.

Variables? (we don't need not stinkin' variables)

The language has no 'named variables'. Instead designated numbered slots accessed with the [environment #] function for read access and [set environment #]'string' for write access. All function names can be shortened to enough characters to distinguish them from other functions. If you search you will not find [environment] anywhere, too much typing. I settled on [envir #] as a long enough form.

Due to the limitation of encoding all compiled functions are encoded into a single byte so there are only 253 distinct functions. Originally this was about four times more than was needed, today this is about half of what is needed. So over the years many functions that did not require a numeric parameter were rolled into a single function that does. It is this single numeric parameter that dictates which function will be executed. To be honest the macro language is not quite "INTERCAL" but with some work could be coerced into being its close cousin.

So now that you are aware of the reason, the [envir] function is a catch all for all kinds of string variables and internal settings controlled by the numeric parameter. Good values to remember are 200-229 which a string values local to the current macro, equivalent to local string variables. In some event "keys" these are also used to pass parameters from WinPTE internal code to the macro. 1-20 being local to the file variable (each file has it own copy), 21-100 being global (static to the current session). Values over 100 give access to all kinds of settings one of which is actually the Process Environment Variables, from which the original function got its name.

There are also equivalent of boolean variables accessed via [set flag #], [clear flag #] and [if flag #] and [if not flag #] for testing. Once again the # parameter controls the context, with 200 - 254 being local to the current invocation of the macro definition, 100-199 being global (static to the session) and 1-99 being local to the file.

There are also the equivalent of an ordered hash table type called a line list. Values can be added/accessed by a numeric index or a string key. Similarly, these line lists take a # argument to identify the list, with 200-209 being local to the macro, 1-10 being local to the file, 10-99 being global. There are only two special # arguments: 230 which refers to the current file's statement completion list and 231 which refers to the default template's statement completion list which by convention contains command line statement completion (at the moment not used very much because there is not much in it).

Control Flow

There is only a single looping construct [do] .... [loop] with an [exit loop] function that terminates the looping. Otherwise all functions between [do] and [loop] will execute in sequence forever or until an error condition is raised or Ctrl+Break key interrupts the macro execution.

There are several [if .... ] conditional functions each evaluating a specific condition or conditions based on a numeric or string parameters.

The general form of conditionals is:

[if ....]

[else]

[endif]

[endif] and [end if] are interchangeable. Additionally sequences of if/else if / else if / ... /end if can be written as

[if ....]

[else if ....]

....

[end if]

There are two generic if functions, one testing whether the next function succeeds the other if it fails:

[if][a function] - tests for success of [a function]

[if not][a function] - tests for failure of [a function]

So as an example the following will move the cursor down until the bottom of file is reached:

[do]

    [if not][down][exit loop][endif]

[loop]

of course the same can be achieved with the [bottom] function with a lot less typing and fixed execution time independent of where the cursor is in the file. But where is the educational value in that?

Accessing Commands from Macros

[execute]'string' function will attempt to execute the string argument as a command, if the string argument is missing or empty then the function will execute the contents for the command line. This is how the ENTER key achieves its magic when you hit ENTER in the command line to execute the command. It invokes the [execute] function with an empty string parameter.

Additionally the [execute] function takes a numeric parameter, if this parameter is > 1 then the executed command will be stored for recall in the command stack, if it is > 2 then the current command stack position will be reset to the top of stack. (used by next/prev command recall functions F9/Shift+F9).

Accessing Macro Functions from the Command Line

The mirror of the [execute] function is the execute command which takes the rest of the command line as a sequence of macro definitions and executes them as a temporary macro. So [execute]'ex [......]' is a long way to invoke a macro from a macro and execute [execute]'a command' the long way to execute a command form the command line. All commands can also be shortened to the minimal number of characters needed to distinguish them from other commands so you will often see ex instead of execute one is just a short form of the full command.

Cursors, Insert Modes, Command Line and Cursor Stack

The current cursor position is represented by the Cursor structure, in the macro various cursor functions manipulate this structure, such as [up] / [down] move the cursor up/down in the file. Similarly [left]/[right] do the same horizontally.

The cursor manipulation functions work identically whether the cursor is in the command line or the text area. So most macros will function correctly in both situations.

Some macros require that the cursor always be in the file area (called the data area as opposed to the command area) the functions that do this is [data cursor] to move to to the file area and [command cursor] to move to to the command line.

Additionally there is a cursor stack that allows saving and recalling of the cursor position and several recall functions to recall only parts of the cursor information. The cursor also stores the current insert/overtype/flex mode, command/data area state, file, line, column and the offset from the cursor to the top edge of the display screen.

Recalling a cursor position restores its position, typing mode, command/data area state and the scroll state of the display. Any of these can be recalled together or one at a time. Cursors can be dropped from the stack (no recall just remove it), or recalled (recall and leave on the stack) they can also be popped of the stack (recall information and remove from the stack).

Cursors on the stack are adjusted by editing operations so the cursor will always be either a legal value for the file it came from or not be recallable at all for example when the file is closed. Because of this adjustment, sometimes you will want the cursor to remain on in the same column position even if preceding characters are deleted (by default the cursor will try to remain on the same text as it was created on, so deleting/inserting characters has the effect of shifting the cursor with the text) there are functions that mark a cursor as:

The recall, pop, drop functions also take a numeric parameter which identifies the offset of the cursor from the top of stack with 1 being the top of stack.

Most macros use the [push cursor]/[pop cursor] to preserve your edit state while flying all over the file or files during execution, changing typing mode and switching to the command line.

Selections

Selections in WinPTE terminology are called marks and come in three flavors: character, line and block. Block being the column selection. Additionally each mark carries a flag that says whether it is a true windows style selection or a persistent selection. WinPTE core makes use of this flag only for the purpose of selecting the right display style when rendering the display. It is the macros that ultimately decide the functionality that you see when you edit files.

The current mark is global to the session and will travel from file to file depending where the text is selected. The file and the command line have their own current mark however functions that operate on each are the same and what determines which one is used is whether the cursor is in the file (or data area ) or the command line. This too ensures that the same macros will work in the file and on the command line without modification (where it makes sense to do so).

The exception to this is the fact that the command line mark is always of character type.

Marks too have a global stack where they are stored and recalled. However, the file and command line marks have their own separate stacks.

Macros use the [push mark]/[pop mark] combinations to save whatever you have selected. They do their own selections while accomplishing their task but you would never notice because they recall whatever you had selected when they were first called.

Like cursors stored marks are modified by editing operations but you have no choice in anchoring them, they always try to remain with their selected text and when this is not possible go out of existence.

There are functions that morph one mark type into another, set/clear the selection flag or simulate window style selections using old style mark terminology. You can also copy, delete, move, shift left/right, justify left/right/left&right and many more operations. There are logical tests to see if a mark exists, exists in current file, is "fresh" or "stale" whether it is visible on screen, test its type, test its block equivalence - which means that it can be a character mark but being a single line character mark it is equivalent to a block mark and vice versa test its character equivalence.

Useful Example: Incremental Search Functionality

I am still working on the detailed macro and command reference but here is a macro implementation of the incremental search with the following features:

You can select and copy the text to the clipboard and add it to an empty file and on the command line execute the 'macro' command. Or you can add this to your custom.pte file (in the installation directory, create a new file if it does not exists and restart WinPTE. thereafter you will have the incremental search function defined on the Ctrl+I key, unless a language specific definition overrides it (to date there is one in C++ template and VB/Lotus Script template)

 Line 
       
    1 
    2 
    3 
    4 
    5 
    6 
    7 
    8 
    9 
   10 
   11 
   12 
   13 
   14 
   15 
   16 
   17 
   18 
   19 
   20 
   21 
   22 
   23 
   24 
   25 
   26 
   27 
   28 
   29 
   30 
   31 
   32 
   33 
   34 
   35 
   36 
   37 
   38 
   39 
   40 
   41 
   42 
   43 
   44 
   45 
   46 
   47 
   48 
   49 
   50 
   51 
   52 
   53 
   54 
   55 
   56 
   57 
   58 
   59 
   60 
   61 
   62 
   63 
   64 
   65 
   66 
   67 
   68 
   69 
   70 
   71 
   72 
   73 
   74 
   75 
   76 
   77 
   78 
   79 
   80 
   81 
   82 
   83 
   84 
   85 
   86 
   87 
   88 
   89 
   90 
   91 
   92 
   93 
   94 
   95 
   96 
   97 
   98 
   99 
  100 
  101 
  102 
  103 
  104 
  105 
  106 
  107 
  108 
  109 
* File: incrsearch.pte, lines 1 to 109
 
    * incremental search macro
    begindef c-i 
        [push cursor]
     
        [set envir 200]''    * search string
        [set envir 203][eval]'0b10001110'
        [set envir 205]''
        [set envir 206]'(down)'
     
        [push cursor]
        [do]
            [clear flag 200]    * direction key pressed, don't loop around
            [error #][envir 203]'Incremental Search '&[envir 206]&': '&[envir 200]&'|'
     
            [set envir 201][key name]
     
            [if escape]
                * drop modified position
                [drop cursor]
     
                * recall original
                [pop cursor]
                [exit loop]
            [endif]
     
            [clear next key]
            [set envir 202][string length][envir 201]
     
            [if eval]'$202>1'
                [if equal]'enter'[envir 201]
                    * drop modified position
                    [drop cursor]
                    [exit loop]
                [else if equal]'backspace'[envir 201]
                    * remove one key
                    [set envir 202][string length][envir 200]
                    [set envir 200][left string #][eval]'$202-1'[envir 200]
                [else if equal]'space'[envir 201]
                    [set envir 200][envir 200]&' '
                [else if equal]'down'[envir 201]
                    [if][down][endif]
                    [set flag 200]
                    [set envir 205]''
                    [set envir 206]'(down)'
                [else if equal]'right'[envir 201]
                    [if][right][endif]
                    [set flag 200]
                    [set envir 205]''
                    [set envir 206]'(down)'
                [else if equal]'up'[envir 201]
                    [if][up][endif]
                    [set flag 200]
                    [set envir 205]'-'
                    [set envir 206]'(up)'
                    [drop cursor]
                    [push cursor]
                [else if equal]'left'[envir 201]
                    [if][left]
                    [else if][up]
                        [end line]
                    [endif]
                    [set flag 200]
                    [set envir 205]'-'
                    [set envir 206]'(up)'
                [endif]
            [else]
                * add it to the search
                [set envir 200][envir 200]&[envir 201]
            [endif]
     
            [if flag 200]
                [drop cursor]
                [push cursor]
            [endif]
     
            [set envir 201]''
     
            [recall cursor]
     
            [set envir 203][eval]'0b10001110'
     
            [if not equal]''[envir 200]
                [if not][locate][envir 200][envir 205]
                    [if not flag 200]
                        [if equal]''[envir 205]
                            [top][begin line]
                        [else]
                            [bottom][end line]
                        [endif]
     
                        [if not][locate][envir 200][envir 205]
                            [set envir 203][eval]'0b10001101'
                            [recall cursor]
                        [else]
                            [center line]
                        [endif]
                    [else]
                        [set envir 203][eval]'0b10001101'
                    [endif]
                [else]
                    [center line]
                [endif]
            [endif]
        [loop]
     
        [error]              * clear the error
        * cursor left on stack so it can be recalled with sa-n
        *[drop cursor]
    enddef 
 

 

 

 

 

 

 

 

Site Map | Privacy Policy | Contact Us | ©2004 Winpte.com