Macros
******

In SM, it is possible to define sets of plot commands as ``subprogrammes'', which can be used just like a plot command, to generate a standard plot.  These plot macros allow variables (e.g. name of the data file, plot label or limits, etc) to be supplied at execution time.

You can also bind commands to keys to save typing; for example I usually bind `cursor' to the PF1 key of my terminal. Such keyboard macros are discussed under KEY and at the bottom of this section.

The macro facility consists of commands to define macros, delete them, write them to disk files, read them from disk files, delete all those macros defined in a specified disk file and list all currently defined macros. In addition, the help command applied to a macro prints out its definition. It is possible to pass up to 9 arguments to a macro, referred to as $1, ... , $9, and in addition $0 gives the name of the macro. While macro arguments are being read they are treated as if they are in sets of {}, except that variables are expanded. If you want to include a space in an argument, enclose it in quotes. If the number of declared arguments is 10 or more, the macro is treated as having a variable number of arguments. If it is 100 or more the last argument extends to the end of the line. For further discussion see the discussion of how macros are used.

A macro is defined by the statement

     MACRO name nargs { body-of-macro }

or

     MACRO name nargs < body-of-macro >

where name may be up to 80 characters, and must not be a keyword (unless you have been playing with OVERLOAD), and body-of-macro is the statements within the macro, and may be up to 2000 characters long. Macros defined using an editor on a file may be up to 10000 characters. If nargs, the number of arguments, is 0 it may be omitted. Macros may also be created using the MACRO EDIT command, which is discussed below, and which is probably easier.  To define the macro in a disk file, the file format must be: the name of the macro starts in the first column, followed by a tab or spaces, followed by the number of arguments, if greater than 0, followed by commands, followed by comments if any.  The next line and any necessary subsequent lines contain the macro definition (starting in a column other than the first one).  Any number of macros may appear in the same file, as long as the macro name is given in the first column and the definition starts in some other column. The first two blanks or tabs are deleted in continuation lines, but any further indentation will survive into the macro definition. Tabs will be replaced by spaces as the macro is read. By default a tab is taken to be 8 characters wide, but this may be changed by specifying tabsize in your .sm file.

When a macro is invoked, by typing its name wherever a command is valid, for example at a prompt, it first reads its arguments from the terminal (if they are not in the lookahead buffer, it will prompt you for them), and defines them as the variables $1, ..., $9, before executing the commands contained within the macro. The number of arguments must be declared correctly. As an alternative it is possible to declare that a macro has a variable number of arguments by declaring 10 or more. The macro will then expect between 0 and the number declared modulo 10 arguments, all on the same line as the macro itself. (i.e. the argument list is terminated by a newline, which may either be a `real' one, or an \n). If the number of arguments is 100 or more it is still reduced modulo 10, but the last argument is taken to be the rest of the line (which may consist of many words). The macro may find out if a particular argument is provided by using $? to see if the variable is defined. For example the macro check, in the format in which it would appear in a file,

     check  11   if($?1 == 1) { echo Arg 1 is $1 }\n

will echo its argument, if it has one, and

     split  102  if($?2 == 0) { DEFINE 2 "(none)" }

     echo $1:$2:

if invoked as split word many arguments will print word:many arguments:. If you add an explicit newline, split word many\n arguments, you'll get word:many: and then a complaint that arguments is not a macro.

If you try to execute a non-existent macro, if it is defined SM will call a special macro called macro_error_handler. It has two arguments; the first is the string NOT_FOUND, and the second is the name of your non-existent macro. When you start SM, the error handler is:

     macro_error_handler 2 ## handle errors associated with macros
     		if($?missing_macro_continue) {
     		   echo $2 is not a macro
     		   RETURN
     		}
     		if('$1' == 'NOT_FOUND') {
     		   del1
     		   define 3 "$2 is not a macro; aborting"
     		} else {
     		   define 3 "Unknown macro error for $2: $1"
     		}
     		USER ABORT $3

which causes an immediate syntax error (the USER ABORT), and remove the errant command from the history list (the del1). You can turn this off by defining the variable $missing_macro_continue, which you can do in your .sm file; this was the default in SM versions 2.1.0 and earlier, and is what you get if the macro macro_error_handler isn't defined.

Unfortunately we can get into trouble with IF's at the end of macros, for much the same reason that RETURN can get into trouble (see Command Internals). The symptoms are that a macro either gets the arguments that were passed to the macro that called it, or complains that it can't handle numbered variables at all because it isn't in a macro at all. To avoid this, there is an explicit \n at the end of the macro check. It is possible to redefine the values of arguments (it won't affect the values you originally specified, arguments are passed by value), or to DEFINE values for arguments that you didn't declare. The latter case allows you to have temporary variables, local in scope to the macro. An example is the rel macro, which is defined as

     rel  2  DEFINE 1 ($1) DEFINE 2 ($2) RELOCATE $1 $2

which allows you to specify expressions to the relocate command. For more examples see the `useful macros' section.

Newlines are allowed within macros, and as usual any text from a # to the next newline is taken to be a comment. If a # is needed within a macro, escape the # with a \ or enclose it in double quotes. If a macro starts with a comment the comment will not affect the macro's speed of execution. Macros starting with ## are treated specially by SAVE (they are not saved) and MACRO LIST (they are not listed if VERBOSE is 0).

If the macro command is given as

     MACRO name { DELETE }

or

     MACRO name DELETE

the macro will be deleted (you can also delete a macro from the macro editor by specifying a negative number of arguments). If the name is already defined, it will be silently redefined. Macros may be nested freely, and even called recursively. For example, the definition

     MACRO aa {aa}

is perfectly legal, but SM will go away and think about it for ever if you ever type aa (or at least until you type C.) The definition

     MACRO zz { zz zz    # comment: not recommended }

is also legal, but in this case if you execute it SM will fill its call and macro stacks and complain when it grabs more space. As before, it will think about it forever. More useful examples of recursive macros are compatible (see Mongo), which starts

     IF($?1 == 0) { compatible 1 RETURN } ...

providing a default value for its argument, and repeat which is discussed under DO.

To find how a particular macro is defined, type HELP macroname. For a listing of the first line of all currently defined macros, type

     LIST MACRO

or

     LIST MACRO x y

The optional x and y are the alphabetical (actually asciial ) range of macro names to list. As mentioned above, if VERBOSE is 0, macros starting with ## are not listed by this command. There is a macro ls defined as DELETE HISTORY LIST MACRO which will list macros without appearing on the history list. (Or you could overload list; see under overload in the index).

A related command is APROPOS pattern which lists all macros and help files (usually; this may not be available on Unix System V and VMS systems.) whose names or initial comments contain the pattern, for example

     APROPOS histogram

would list bar_hist and get_hist as well as the abbreviations hi and hist. If you wanted to find all macros starting with a single comment character which mentioned histogram you could say

     APROPOS "^#[^#] .*histogram"

where the double quotes prevent the #'s being interpreted as comment characters.

     APROPOS ^[a-z]

will list all macros beginning with lowercase letters -- this is similar to MACRO LIST a z, but pays no attention to the value of VERBOSE.

It is also possible to read macros in from disk, and in fact when SM is started, it tries to read the file default in the directory specified by macro in the environment file .sm. The command to read a file of macros is

     MACRO READ filename

Any line with a # in the first column is treated as a comment, and is echoed to the terminal if VERBOSE is greater than zero. All the currently defined macros may be written to a file with the command

     MACRO WRITE filename

If the file exists, it will be overwritten (under VMS, a new version of the file will be written). Macros are written out in alphabetical order.

The command

     MACRO WRITE macroname filename

writes the macro macroname to the file filename. This command remembers which file it last wrote a macro to, and if the current filename is the same then it appends the macro to the end of the file, otherwise it overwrites it (or creates a new version under VMS) unless the filename is preceded by a +, in which case the macros will always be appended. This allows a set of related macros to be written to a file.

     MACRO DELETE filename

undefined all macros which are defined in filename. This allows a file of macros to be read in, used and forgotten again. The difference between this command and MACRO macroname DELETE should be noted. The SAVE command is probably a better way of saving all current macros. The format of macros on disk is name nargs text, where nargs may be omitted if it is 0. Continuation lines start with a space or tab. See the files in the directory specified by macro in your .sm file for examples.

It is possible to define macros from the history list. The command

     MACRO name i j

defines a macro name to be lines i to j inclusive of the history list, as seen using HISTORY. The opposite of this command, which places a macro upon the history list, is WRITE HISTORY name. Examples of these commands are the macros playback and edit_hist given in the section `A Simple Plot'. This way of defining macros can be convenient if you have created a useful set of commands on the history buffer, and now want to save it in a macro and go on to other things. Editing the playback buffer, and then changing its name to something else (see next paragraph) is a convenient way of saving it that implicitly uses this command.

Macros may be edited, using essentially the same keys as described above for the history editor. The command MACRO EDIT name starts up the editor, which works on one line at a time. (you might prefer to use the macro ed which is equivalent to MACRO EDIT, but doesn't appear on the history list and, if invoked without a macro name, will edit the same macro as last time. In addition, you can list the current macro with hm.) The zeroth line has the format

     0> Macro name : Number of arguments: n

where name is the name of the macro, and n is the number of arguments to the macro. If this line is changed, except to change name or n, any changes made to the macro will be ignored (note that the space after name is required). This can be useful if you decide that you didn't want to make those changes after all. Changing name or n has the obvious effect, except that if n is negative the macro is deleted when you exit the editor. An empty macro is also deleted when you leave the editor (i.e. one with no characters in it, not even a space). The first line that you are presented with is the first line in the macro rather than this special one. Use N (or down-arrow ) to get the next line, P (or up-arrow ) to get the previous line. Carriage return (M) inserts a new line before the cursor, breaking the line in the process, while O inserts a new line before the current line. To save the changes and return to the command interpreter use X. All other keys have the same meaning as for the history editor (e.g. A to go to the beginning of a line). Note that K and Y can be used to copy lines, and that the bindings can be changed with EDIT or READ EDIT.

It wouldn't be hard to write a macro that wrote out a macro to a file, invoked your favourite text editor, then read the new definition back in; see the macro emacs_all for ideas.

It is sometimes convenient to define a key to be equivalent to typing some string, such as playback or cursor. This can be done with the KEY command, whose syntax is KEY key string. If you just type KEY <CR> you'll be prompted for the key and string. In this case you are not using the history editor to read the key, and you can simply hit the desired key followed by a space and the desired string, terminated by a carriage return. If you put KEY, key and string on one line you'll probably have to quote the key with Q or ESC-q, or write the escape sequences out in the way used by EDIT. If this sounds confusing, here is an example. Type KEY<CR>, then hit the PF1 key on your terminal, type a space, and type "echo Hello World\N". Now just hit the PF1 key and see what happens. (The closing \N meant `and put a newline at the end'). These keyboard macros are not generally terminal independent, but they can be convenient. Definitions for the `PF' keys on your keyboard can be made in a terminal-independent way by specifying the key as pfn or PFn where n is 1, 2, 3, or 4. If you always use the same terminal you might want to put some KEY definitions in your private startup file (see the discussion of startup2 in the section on useful macros). The current KEY definitions are listed with the LIST EDIT command, along with the other key bindings.

DO and FOREACH loops, WHILE loops, and IF statements
****************************************************

Related to the macro facility are the DO, FOREACH, and WHILE commands. IF is included here as a flow-of-control keyword.

The syntax for a DO loop is

     DO variable = expr1 , expr2 [ , expr3 ] { command_list }

where the third expression is optional, defaulting to 1. The value of variable ($variable) is in turn set to expr1, expr1+exp3, ..., expr2, and the commands in command_list executed. Changing the value of $variable within the command list has no effect upon the loop. Do loops may be nested, but the name of the variable in each such loop must be distinct. A trivial example would be

     DO val = 123, 123+10, 2 { WRITE STANDARD $val }

while a more interesting example would be the macro square discussed in the section on examples.

For loops within macros, it's often a good idea to make the loop variable local: DEFINE val LOCAL DO val ... (see Define).

Because the body of the loop must be scanned (and parsed) repeatedly, loops with many circuits are rather slow. If at all possible you should try to use vector operations rather than DO loops. For example the loop

     DO i=0,DIMEN(x)-1 {
          SET x[$i]=SQRT(x[$i]) IF(x[$i] > 0)
          SET x[$i]=0 IF(x[$i] <= 0)
     }

is better written as

     SET x=(x > 0) ? SQRT(x) : 0

where the ternary operator ?: is discussed in the section on vectors (see Vectors).

As an alternative to DO loops, SM also has a general looping command, a while loop, for example

     set i=0
     while {i < 10} {
             commands
             set i=i+1
     }

is equivalent to a DO loop. In addition to being more flexible, WHILE loops may also be interrupted with the BREAK command, so the previous example could have been written (using variables instead of vectors)

     define i 0
     while{1} {
             echo Hi $i
             define i ($i+1)
             if($i == 10) { break }
     }

Foreach loops are similar to do loops, with syntax

     FOREACH variable ( list ) { command_list }

,

     FOREACH variable { list } { command_list }

, or

     FOREACH variable vector { command_list }

where the list may consist of a number of words or numbers. a vector. Each element in the list (or of the vector) is in turn defined to be the value of $variable, and then the commands in command_list are executed, so that for example the commands:

     FOREACH i ( one 2 three ) { WRITE STANDARD $i }
     SET str = {one 2 three}
     FOREACH i str { WRITE STANDARD $i }

will print out:

     one
     2
     three

twice. Foreach loops may be nested, but again the variables must be distinct. You can delimit the list with {} so that it can include keywords (and other things that you want treated as strings such as 0.1 or $date), but even then you can't have the word delete in the list of a foreach. Sorry. It's often helpful to make the foreach variable LOCAL (see Set) inside macros, e.g.

     DEFINE var LOCAL  FOREACH var ...

If statements look similar, with syntax

     IF ( expr ) { list } ELSE { list2 }

where the ELSE clause is optional, but if it is omitted the closing } must be followed by a newline (or explicit \n) (see Command Internals).

If the (scalar) expression is true (i.e. non-zero), then the commands list are executed, otherwise list2 is, if present. It is also possible to use IF statements directly in plotting commands, for example POINTS x y IF(z > 1/x).

It is possible to write general loops in SM by using of tail-recursive macros. (This was the only way before the days of WHILE loops (see While), and as the technique is interesting, we've kept this section in the manual) The simplest example would be

     macro aa {echo hello, world\n aa}

which prints hello, world and then calls itself, so it prints hello, world and then calls itself, and so on until you hit C. The absence of a space before the closing brace is very important, as it allows SM to discard the macro before calling it again, which means that it won't fill up its call stack and start complaining. A more interesting example is the macro repeat which repeats a given macro while the given condition is true. For example, if you say

     macro aa { set i=i+1 calc i }
     set i=0 repeat aa while i < 10

it will print the integers from 0 to 9. With a few checks, bells, and whistles the macro looks like:

     repeat 103      # Repeat a macro `name' while the condition is true
                     # syntax: repeat name while condition
                     # Example: set i=0 repeat body while i < 10
                     if('$2' != 'while') {
                        echo Syntax: $0 macro while condition
                        return
                     }
                     if(int((whatis($1)-4*int(whatis($1)/4))/2) == 0) {
                        echo $1 is not a macro
                        return
                     }
                     macro _$1 {
                        if(($!!3) == 0) { return }
                        $!!1
                        _$!!1}
                     _$1
                     macro _$1 delete

and is one of SM's default macros (type ``help repeat'' if you don't believe me).

The Help Command
****************

There is an online help command. Typing HELP <CR> or HELP HELP gives a list of the help menu, or HELP keyword gives help with that keyword. The help menu consists of any files in the directory specified by the entry help in the environment file, so for example HELP data types the file data in that directory. For all cases except HELP help, the file is filtered through a version of the Unix utility more which pages the file. When more offers you a `...' prompt, type ? to see your options. The same filter is used by e.g. LIST MACRO . If the command is HELP word, after HELP tries to print the file word, it looks to see if word is a macro, and if so prints its definition. If word is a variable, its value is then printed, and if word is also a vector HELP prints the dimension, followed by the help string associated with the vector vector_name (see section on vectors).

The APROPOS command is also useful when you need help. APROPOS string scans all the help files (if your operating system allows SM to do such things) and macro headers looking for the string. The string may actually be a pattern (see the description of APROPOS for details). If VERBOSE (see Verbose) is zero only the lines from the help files matching the pattern are printed; if it is larger you are given a couple of lines of context on each side.

It is worth remembering that the index to this manual has an entry under weird, wherein are listed all sorts of strange happenings, with explanations and suggestions for workarounds.