Examples of Useful Macros
*************************

When you start SM the directory specified as macro in your .sm file is searched for a file default, and then the macro startup  from that file is executed. At the time of writing of this manual, startup was defined as:

     startup      ## macro invoked upon startup
                  FOREACH 1 { default_font device edit file_type history_char \
                         macro macro2 overload printer prompt prompt2 SHELL } {
                     DEFINE $1 :
                  }
                  FOREACH 1 { TeX_strings case_fold_search fan_compress \
                      line_up_exponents noclobber overload \
                      remember_history_line traceback uppercase } {
                     DEFINE $1 :
                     IF($?$1) {
                        IF('$$1' == '0') {
                           DEFINE $1 DELETE
                        }
                     }
                  }
                  IF($?prompt) { PROMPT $prompt\n DEFINE prompt DELETE
                  } ELSE { PROMPT : }
                  IF($?device) { DEVICE $device
                  } ELSE { DEFINE device nodevice }
                  IF($?default_font && $?TeX_strings == 0) {
                     echo You can only define a default font if you use TeX
                  }
                  IF($?history_char) {   # use $history_char as history character
                     IF('$history_char' != '0' && '$history_char' != '1') {
                        EDIT history_char $history_char
                     }
                     EDIT ^ ^
                     DEFINE history_char DELETE
                  }
                  # load the default macros
                  DEFINE mfiles < stats utils > # $mfiles is used by `sav'
                  FOREACH f ( mongo $mfiles ) { MACRO READ "$!macro"$f }
                  FOREACH var ( x_col y_col data_file ) { DEFINE $var . }
                  # load uppercase if defined in .sm file
                  IF($?uppercase) {
                     MACRO READ "$!macro"uppercase
                  }
                  # and overload keywords such as erase, if so desired
                  set_overload $?overload
                  # and some keymaps
                  IF($?edit) { READ EDIT "$!edit" }
                  # and an optional macro file, with macro startup2
                  IF($?macro2) {
                     MACRO READ "$!macro2"default
                     IF(is_set(startup2,1)) { startup2 }  # startup2 is defined
                  }
                  # provide a \n after the IF

As this macro is executed every time that you run SM, let us consider it in some detail. After setting the prompt, it looks for entries for a number of variables in your .sm file. Some (such as printer) are simply DEFINEd, while some (such as TeX_strings) are only DEFINEd if they have a non-zero value. Because some of the values might not be numeric, the comparison is forced to be done on strings by enclosing the quantities in single quotes. An entry prompt is interpreted as a primary prompt, mostly for compatibility with the use of $prompt2 to set the secondary prompt. If device is defined it is used to set the default plotting device, and both it and printer are used by a couple of macros (hcopy and hmacro) that produce hardcopy. The variables TeX_strings and default_fonts are used in producing labels (see Fonts). Because TeX uses ^ for superscripts, we allow you to put a history_char line in .sm to specify a character to use rather than ^ for history (I use `). If you use 0, or omit the value (so it is set to 1), no history character is defined to replace ^. The variable file_type is used by the IMAGE command to determine the file format that you use (e.g. C, or unformatted fortran).

Startup doesn't have to check that macro was successfully defined as it must have been found for startup to have been read in the first place. Macro specifies where to look for macro libraries, and startup next sets the variable mfiles containing the names of some of the system macros to be loaded, and reads them. The macro load defined below also maintains the mfiles list, as does unload. It is used by the sav macro, which is discussed below the main listing of macros that follows. We also set some variables used by the id macro.

As part of our effort to be nice to users, if you have uppercase 1 in your .sm file, we also load the uppercase macros. Next startup overloads some keywords if overload is in your .sm file, reads a file of keybindings (if edit is given in .sm), and finally tries to read a second optional macro directory macro2, and executes a macro startup2 if it's defined (that's what the macro is_set is checking). This is quite important, as it provides a way to customise SM to your personal taste without convincing the local SM guru that your taste should be foisted on everyone. If you want a prompt that is different, or a definition of q that just quits without asking questions, you can get them by using macro2. You can see that it is possible to tailor SM pretty much as you wish without changing a line of code, just by playing with the startup macro.

SM provides various compatibility macros, and some to package often-used functions. The macro files stats and utils, which are read when SM is started,  provide various useful macros, a few of which are presented here. To see a current list, either look at the files directly, set VERBOSE to zero and list all the macros, look at the listing in this manual (see Libraries), or use lsm to list macro files (this only works if you are running Unix; try lsm demos). We give here a number of macros taken from the files default, mongo, stats, and utils. Among those not listed are those like lin defined to be lines that are pure abbreviations, those like xlogarithm defined as SET x=lg(x) which provide functionality in a perhaps familiar form, and many more like those that are given here which provide enhancements (e.g.   the macro barhist). A discussion of a few of the more interesting or obscure follows. Keywords are written in uppercase, because you might have been playing tricks with overloading the lowercase equivalents. Many of these macros, in fact all from default and mongo, start with ## so as not to show up in listings made when VERBOSE is 0, and so as not to be SAVEd. In the interest of brevity we have omitted most of these initial comments.

     cumulate 2   # Find the cumulative distribution of $1 in $2
                  DEFINE sum 0 SET $2=0*$1 SET HELP $2 Cumulation of $1
                  DO i=0,DIMEN($1)-1 {
                     DEFINE sum ( $sum + $1[$i] )
                     SET $2[$i] = $sum
                  }
                  define sum delete
     da   1       DATA "$1"
     del1 1       DELETE HISTORY \n
     dev  1       del1 DEFINE device $1 DEVICE $1
     dra 2        # Draw, accepting expressions
                  define 1 ($1) define 2 ($2) draw $1 $2
     edit_hist    # Edit the history list
                  del1 MACRO all 0 100000       # define "all" from buffer
                  WRITE STANDARD Editing History Buffer\n
                  MACRO EDIT all                # do the editing
                  DELETE 0 100000       # empty history buffer
                  WRITE HISTORY all             # replace history by "all"
     era          del1 ERASE
     gauss 1      # Evaluate a Gaussian : N($mean,$sig)
                  SET $0 = 1/(SQRT(2*PI)*$sig)*EXP(-(($1-$mean)/$sig)**2/2)
     get 2        # Syntax: get i j.  Read a column from a file.
                  # Name of vector is jth word of line i.
                  DEFINE nn READ $1 $2 echo reading $nn\n
                  READ $nn $2
                  SET HELP $nn Column $2 from $data_file
                  DEFINE nn DELETE
     hardcopy     DEVICE nodevice   # close old device
     hcopy 13     ## hcopy [printer] [l1] [l2] Make hardcopy of playback buffer
                  # optionally specify printer ($1) and desired lines ($2-$3)
                  # if the printer ($1) is omitted (i.e. $1 is missing or a
                  # number), it will be taken from the value of the environment
                  # variable PRINTER, if defined.
                  IF($?printer == 0) {
                     DEFINE printer ? { what kind of printer? }
                  }
                  IF($?1) {
                     IF(WHATIS($1) == 0) { # a number
                        if($?2) { DEFINE 3 $2 }
                        DEFINE 2 $1
                        DEFINE 1 DELETE
                     }
                  }
                  IF($?1) {
                     DEVICE $printer $1
                  } ELSE {
                     IF($?PRINTER == 0) { DEFINE PRINTER : } # which one?
                     IF($?PRINTER) {
                        DEVICE $printer $PRINTER
                     } ELSE {
                        DEVICE $printer
                     }
                  }
                  IF($?2 == 0) {
                     DEFINE 2 0 DEFINE 3 10000
                  } ELSE {
                     IF($?3 == 0) { DEFINE 3 $2 }
                  }
                  playback $2 $3 \n DEVICE $device
                  bell
     hmacro  12   ## hmacro [macro] [printer] Make hardcopy of a macro
                  # If only 1 argument is present, it is taken to be the printer
                  # unless an environment PRINTER variable is defined, when
                  # that's used as a printer, and the argument is taken to be
                  # a macro. If no macro is specified, make a temp one
                  IF($?printer == 0) {
                     DEFINE printer ? { what kind of printer? }
                  }
                  del1
                  IF($?2 == 0) {          # only one arg
                     IF($?PRINTER == 0) { DEFINE PRINTER : }
                     IF($?PRINTER) {
                        DEFINE 2 $PRINTER
                     }
                  }
                  IF($?1) {
                     if($?2) {            # 2 args
                        DEFINE _mac $1
                        DEFINE _temp 0    # no temp macro
                     } ELSE {             # 1 arg, take as printer
                        DEFINE 2 $1       # printer
                        DEFINE _temp 1    # need temp macro
                     }
                  } ELSE {                # no $1
                     IF($?2 == 0) { DEFINE 2 " " }
                     DEFINE _temp 1       # need temp macro
                  }
                  IF($_temp) {
                     DEFINE _mac _mac
                     echo "Create temporary macro, exit with ^X"
                     MACRO EDIT $_mac
                     IF(is_set($_mac,1) == 0) {
                        DEFINE _mac DELETE DEFINE _temp DELETE
                        DEFINE _test DELETE
                        RETURN
                     }
                  }
                  DEVICE $printer $2
                  $_mac \n DEVICE $device
                  IF($_temp) { MACRO $_mac DELETE }
                  DEFINE _mac DELETE DEFINE _temp DELETE bell
     load         # load macros in default directory
                  DEFINE macro :                   # get default directory
                  MACRO READ "$!macro"$1  # read macro file
                  IF($?mfiles == 0) {
                     DEFINE mfiles $1
                  } ELSE {
                     DEFINE 3 0
                     FOREACH 2 ( $mfiles ) {
                       IF('$2' == '$1') { DEFINE 3 1 }
                     }
                     IF($3 == 0) { DEFINE mfiles < $mfiles $1 > }
                  }
     load2 1      # load macros in (second) default directory
                  DEFINE macro2 : # get directory
                  IF($?macro2) {
                     MACRO READ "$!macro2"$1  # read macro file
                  } ELSE {
                     echo Directory macro2 is not defined
                  }
     logerr 3     # logerr x y error, where y is logged, and error isn't
                  SET _y = 10**$2
                  SET d_y = LG(_y + $3) - $2 ERRORBAR $1 $2 d_y 2
                  SET d_y = $2 - LG(_y - $3) ERRORBAR $1 $2 d_y 4
                  DELETE _y DELETE d_y
     lsq  15      # do a least squares fit to a set of vectors
                  # syntax: lsq x y [ x2 y2 [rms]] Fit line y2=$a*x2+$b to x y
                  # optionally, calculate rms residual as $rms
                  # see rxy to find product moment correlation coeff,
                  # and spear for Spearman's corr. coeff., and significance
                  SET _n = DIMEN($1)              # number of points
                  SET _sx = SUM($1)               # sigma x
                  SET _sy = SUM($2)               # sigma y
                  SET _sxy = SUM($1*$2)           # sigma xy
                  SET _sxx = SUM($1*$1)           # sigma xx
                  DEFINE a ( (_n*_sxy - _sx*_sy)/(_n*_sxx - _sx*_sx) )
                  DEFINE b ( (_sy - $a*_sx)/_n )
                  IF($?3 && $?4) {
                     SET $4=$a*$3+$b
                     IF($?5) {
                        DEFINE $5 ( sqrt(sum(($a*$1 + $b - $2)**2)/dimen($2)) )
                     }
                  }
                  FOREACH v ( _n _sx _sy _sxy _sxx ) { DELETE $v }
     playback     ## define "all" from buffer, and run it
                  # with args, only playback those lines
                  IF($?1 == 0) {
                     DEFINE 1 0 DEFINE 2 10000
                  } ELSE {
                     IF($?2 == 0) { DEFINE 2 $1 }
                  }
                  del1 MACRO all $1 $2 all
     read_old 1   del1 # read a Mongo file onto the history buffer
                  READ OLD temp $1
                  WRITE HISTORY temp MACRO temp { DELETE }
     rel 2        # Relocate, accepting expressions
                  define 1 ($1) define 2 ($2) relocate $1 $2
     reverse 1    # reverse the order of a vector
                  SET _i = DIMEN($1),1,-1 SORT < _i $1 > DELETE _i
     sav 1        # Save to a file $1, don't save from files `$mfiles'
                  _save $1
     _save 1      # Save to a file $1, don't save from files `$mfiles'
                  del1
                  FOREACH 2 ( $mfiles ) { MACRO DELETE "$!macro"$2 }
                  DEFINE 2 0 define 2 ? { save vectors? }
                  SAVE "$!1" 1 $2 1
                  FOREACH 2 ( $mfiles ) { MACRO READ "$!macro"$2 }

Cumulate is given as a way not to write macros if you can help it (in this case, I couldn't). A better example is reverse which reverses the order of the elements in a vector without resorting to a DO loop.

The macro da could have been defined to be DATA, but there are various special characters that appear in filenames; try data /usr/spool/junk or data disk$data:[ETHELRED]junk.dat. The macro da provides a set of double quotes to escape these unwanted interpretations. Incidently, da "/usr/spool/junk" won't work.

DELETE HISTORY deletes the last command on the history buffer, so del1 alone on a line will delete itself, which can be used to prevent a command from appearing on the history list, for example changing devices with dev; dev also defines a variable device which is used by the hcopy  and hmacro macros to make hardcopies, while returning you to your initial device. The startup macro listed above also sets device, if it is specified in your .sm file. You should be careful not to include more than one del1 macro in any macro that you write yourself, as each del1 will remove a command from history and you could find commands mysteriously disappearing.

Gauss evaluates a Gaussian, e.g. SET x=-3,3,0.05 SET g=gauss(x) lim x g box con x g, an example of using a macro like a function definition. (For this example to work, you have to define variables mean and sig first).

There is an example of reading variables from files and using them in macro get. This reads a word from a line in a file with the DEFINE nn READ i j command, which sets $nn to be the jth word on line i of the current data file. This variable is then used to READ a vector, which is given the appropriate name. So if a file looks like:

     This is an example file
     alpha   beta    gamma   delta
     1       10      0.1     1e1
     2       20      0.2     1e2
     3       30      0.3     1e3
     4       40      0.4     1e4
     5       50      0.5     1e5

then the commands

     GET 2 1   GET 2 2   GET 2 3   GET 2 4

will read `1 2 3 4 5' into vector alpha, `10 20 30 40 50' into beta and so forth. Note that

     DEFINE READ file_id 1 LABEL $file_id

will write out `This is an example file' to the current position of the plot pointer (see, e.g.   RELOCATE). Incidently, READ ROW omega 5 would set the vector omega to have values `3 30 0.3 1e3'.

The macros hcopy and hmacro make hardcopies of, respectively, the playback buffer and a macro. Both assume that the variables device and printer are set. device is set from your .sm file and by the dev macro; printer is assumed set in .sm. (See `startup' file above). If all is well, the macros switch to device printer (with an argument to specify which sub-printer is desired. We have so many laser printers here...), execute the desired commands, and return to the initial device. When the printer device is closed, hardcopy will result. Note the use of \n to ensure that no nasty things happen; if there were no \n and the buffer ended with LABEL Hi, the plot could appear with a label Hi device tek4010. The versions of hcopy and hmacro given here accept a variable number of arguments (`13' means up to 3 arguments). The first (if present) is taken to be the desired laser printer (Actually, if the environment (VMS: logical) .sm variable PRINTER is defined the macros pretend that it was the first argument, so you can simply type hcopy.), the next argument is the number of the first line that you want played back, and the third is the last line number. (If you omit both line numbers you'll get the whole buffer; if you omit the second you'll just get the one line). The macro sees what it has been given by using $? to see which variables are defined, and acts accordingly. hmacro is somewhat similar, except that if you omit an argument it is taken to be the macro name, and a temporary one is created for you. The playback macro deals with its arguments in a similar way, and is discussed further in the examples at the end of this section.

load enables you to read a set of macros from a directory specified as macro in your environment file. Load2 is similar, but it looks in directory macro2. The macro unload (not listed here) will undefine the loaded macros. Note that a list of all the loaded macros is kept in $mlist, which is used by the sav macro to avoid SAVEing lots of system macros. sav is written in terms of a macro _save so that it won't itself be forgotten (by MACRO DELETE) while in the middle of saving macros.

If you want to put errorbars on logarithmic plots, logerr is the macro you've been looking for. It calculates the correct length for the errorbars, and plots them de-logging and re-logging as appropriate.

The macros rel and dra illustrate a method of using expressions, rather than numbers, in the commands RELOCATE and DRAW. There are Good Reasons why DRAW won't accept an expression directly (see Command Internals). These macros exploit the fact that the arguments to a macro are whitespace delimited, so a string such as 1+2/$x comprises one argument. Redefining the arguments means that the macros don't have to define, and then delete, a couple of variables to hold the expressions.

Now that you have had your appetite whetted, we strongly recommend that you take the time to look through the other macros that are available (see Libraries). Otherwise how would you know that there are macros to draw arrows on plots, do KS and Wilcoxon tests on vectors, and a host of other good things?

     [file examples.txi not included]

What Quotes What When
*********************

This chapter is not really needed as all of its contents can be found elsewhere in this manual, but as people manage to become confused anyway, here's a summary. There are three ways in which characters can alter SM's behaviour: they can affect the way that characters and keywords are interpreted, they can be special to the grammar, and they can perform both functions. If you are confused, you might find a verbosity of four or five helpful.

"..."
     Turn off the expansion of variables, the significance of mathematical operators such as / or :, and the recognition of keywords. For example, after DEFINE rhl Patricia, /data/$rhl would be interpreted as four tokens (/, data, /, and Patricia), while "/data/$rhl" is only one (/data/$rhl). Note that in the former case, the data will be taken to be part of a DATA command, and may well lead to a syntax error. If you need to force a variable to be expanded within double quotes, use an exclamation mark: "$!rhl". Double quotes have no syntactical significance.

'...'
     Single quotes surround strings which may contain white space (spaces or tabs), characters such as + or /, or keywords. They don't affect variable expansion, so you have to enclose them in double quotes if you want to protect them. Of course, you must make sure that you are not quoting the single quotes -- use '"$abcd"' rather than "'$abcd'" . Strings are significant to the grammar so only use single quotes where they are needed. For example DATA 'my_file' is a syntax error, and SET s='abc' and SET s=abc mean quite different things.

{...}
     Turn off all interpretation of special characters and keywords, just like double quotes. In fact, braces even turn off the expansion of $!var, to expand a variable within braces you need to say $!!var but you only very seldom need this. Braces have syntactical significance, delimiting lists. Wherever you can use braces you can use angle brackets.

<...>
     Almost identical to {...}, but don't turn off variable expansions. In fact, <> don't always turn off keyword interpretation -- specifically they only do so in str_list's (if you want to know what this means you'll have to read SM's grammar in file control.y). In practice you'll probably only meet this if you try saying Foreach f < quit when ahead > { echo $f }.

(...)
     Parentheses don't turn off any sort of expansion, but can have syntactical significance. You are most likely to see this in DEFINE var ( 1 + 2 ) where the parentheses tell SM to evaluate the expression before defining the variable var as 3. Note that DEFINE var < 1 + 2 > defines var as  1 + 2 .

$name
     Expand name as a variable. Expansion is turned off within single and double quotes and within braces. Because the expansion happens before the parser sees the input, variables cannot have any syntactical significance as the parser never knows about them. This means that variables could be used to make SM look like something quite different; for example after DEFINE begin "{ " DEFINE end "} ", you can say

          MACRO hi $begin echo Hello World $!!end

     (This is a little tricky. The spaces in DEFINE begin "{ " are needed so that SM is still in quote mode when it processes the {. You can probably figure out why I need to say $!!end -- the !! should be a Helpful Hint.)

$(expr)
     Evaluate the expression expr and substitute the resulting value. This is identical to DEFINE temp ( expr ) $temp, but much more convenient.

As a reasonably complex example, try to guess the output from:

     DEFINE hi {<"$!('Hello')" World>} DEFINE hello $hi echo :$hi:$hello:

If you are not sure, try it (you might find VERBOSE 5 helpful) (The {} turn off all expansions, so hi is defined to be <"$!('Hello')" World>. The statement defining hello then becomes DEFINE hello <"$!('Hello')" World>, and the `!' ensures that the expression 'Hello' is evaluated and substituted; 'Hello' is a string valued vector, admittedly with only one element, so it evaluates to the five characters `Hello' and the statement becomes DEFINE hello <Hello World> which means that hello is defined as Hello World. Apart from being a red herring, the colons are only there to make it easy to see which variable expands to what.).

Reserved Keywords
*****************

Keywords are reserved, so don't try to use them as macro names or whatever. You can use the lowercase forms if you explicitly ask to be allowed to overload them. The control words are :

     WORD STRING FLOAT INTEGER ( ) { } \n ! APROPOS BREAK CHDIR DEFINE DELETE DO
     EDIT ELSE FOREACH HELP HISTORY IF KEY LIST LOCAL MACRO OLD OVERLOAD PRINT
     PROMPT QUIT READ RESTORE RETURN ROW SAVE SET STANDARD TABLE TERMTYPE USER
     VERBOSE WHILE WRITE

Arithmetic words are :

     ABS ACOS ASIN ATAN ATAN2 CONCAT COS DIMEN EXP FFT INT LG LN PI RANDOM
     SIN SQRT STRLEN SUM TAN WHATIS POWER_SYMBOL [ ] = + - * ** / < > | & ? :

SM plotting keywords are:

     ANGLE ASPECT AXIS BOX CONNECT CONTOUR CTYPE CURSOR DATA DEVICE DOT DRAW
     ERASE ERRORBAR EXPAND FORMAT GRID HISTOGRAM IMAGE LABEL LEVELS LIMITS
     LINES LOCATION LTYPE LWEIGHT MINMAX NOTATION POINTS PTYPE PUTLABEL RANGE
     RELOCATE SHADE SORT SPLINE TICKSIZE WHATIS WINDOW XLABEL YLABEL

With the exception of POWER_SYMBOL (which is equivalent to **) these are described below, with the convention that arguments between square brackets are optional. This has nothing to do with their use in subscripting arrays!