You are here: Home Users and Science Experimental facilities Technical Beamline Support BLISS Documentation Spec Tutorial BLISS: Spec Tutorial CH2 Macros

BLISS: Spec Tutorial CH2 Macros

last modified 05-06-2007 17:58

SPEC Macros

In the previous chapter you learnt that the SPEC interface to users is an interpreted language that allows you to send a big variety of commands. You learnt as well how to execute a sequence of commands by creating a file that contents such a sequence and loading it afterwards. But a considerable part of the power of SPEC is the possibility to create new commands that then become part of the language, this is what we will call, from now on: macros. In fact, many of the commands you learnt already they are nothing but macros.

In this chapter you will learn how to create macros and a number of commands to manipulate, list or change them. We will discuss about the two types of macros that exist, traditional macros and macro functions (This will be the subject of one of our advanced topics paragraph). If you are interested in SPEC administration details you may find interesting, at the end of the chapter, the description of how SPEC automatically loads standard and ESRF distributed macros.

Three other sections complete the discussion about macros. In one of them you will find some other utility macros for working with macros. A second one includes a full, more sophisticated example, taken from a real macro file used somewhere at ESRF beamlines. Finally a number of opened questions and exercises are proposed that might serve to test if you understood what it is explained here ant to help you to go further.


The def directive: defining macros

All through this chapter the difference is made between traditional macros and macro functions. Even if, in the essential, both work in much the same way, there will be a few differences in the definition, passing parameters and the way to return values. In the advanced topics below it will explain how SPEC internally uses one and the other type of macros in a very different way.

The syntax to define a macro is:
  • traditional:
    def macroname '
    sequence of commands
    '
  • macro function:
    def macroname(parameters) '{
    sequence of commands
    }'

The sequence of commands can be any arbitrary sequence of variable declarations and assignments, SPEC flow control directives, SPEC built-in functions, comments and macros:

94.SPEC> def speed ‘{
95.quot> local distance elapsedt average
96.quot>
96.quot> distance = 12.7
97.quot> elapsedt = 26.5
97.quot>
98.quot> average = distance / ( elapsedt / 60 )
99.quot> print "Average speed: " average "km/h"
100.quot> }’

Remark that, even if curly brackets are not necessary for defining a traditional macro, it is strongly recommended to always use them for clarity and to avoid problems derived from the way SPEC interprets macros. (see: advanced topics later in this section).
You can then invoke the macro by typing its name:
101.SPEC> speed
Average speed: 28.7547 km/h
One of the clear advantages of a macro is the possibility of executing it several times with different execution values. These values can be changed either with global variables, by prompting the user for values or by calling the macro with different parameters:
The speed macro using global variables becomes:
def speed {
local average
average = TUT_DIST / ( TUT_ELAPSED / 60 )
print "Average speed: " average
}
then the same result is obtained by typing:
102.SPEC> TUT_DIST = 12.7
103.SPEC> TUT_ELAPSED = 26.5
104.SPEC> speed
Average speed: 28.7547 km/h
Or it could have been written like:
def speed '{
local distance elapsedt average
print "This macro calculates average speed"
distance = getval("Total distance (kms)",0)
elapsedt = getval("Elapsed time (min)",0)

average = distance / ( elapsedt / 60 )
print "Average speed: " average
}'

105.SPEC> speed
This macro calculates average speed
Total distance (kms) (0)? 12.7
Elapsed time (min) (0)? 26.5
Average speed: 28.7547

The command getval used in this macro is a SPEC built-in function that allows to ask the user for values with a prompt string and a default value.

Providing input parameters to macros

The normal way to pass values to a macro is by giving parameters in the command line together with the call to the macro. Traditional macros and macro functions differs in the way this is done and in how parameters are treated later. Each of them is explained separately:

Parameters to traditional macros

the parameters are given in the command line just after the macro call and separated with spaces.

106.SPEC> speed 12.7 26.5

The macro has to be redefined to receive them:

def speed '{
local distance elapsedt average

if ( $# != 2) {
print "Usage: speed distance elapsed-time"
exit
}

print "This macro calculates average speed"
distance = $1
elapsedt = $2

average = distance / ( elapsedt / 60 )

print "Average speed: " average

}'

When SPEC will execute this macro all the $x will be substituted before execution. In this way $1 will be substituted by the first parameter in the macro call and $2 by the second. $# will be replaced with the total number of parameters that were passed to the macro.

The following symbols are used inside a macro to interpret input parameters:

  • $1..$24 : The parameter typed by the user. If a parameter is not typed in, its value is 0. The maximum number of parameters that can be given to a macro in this way is limited to 24.
  • $# : The number of parameters given by the user.
  • $* : All the parameters separated by blanks.

If you want to assign a variable with the literal character $, you must precede the dollar sign with a backlash:

variable = "\$"

Because SPEC substitutes the parameters before doing any evaluation there is a number of points to be noted. If your parameter contains a string literal, $x, it has to be surrounded by quotes, "$i", otherwise the value of the string will be interpreted as a variable name.

def hello '{
print $1
}

If you call this macro like:

100.SPEC> hello peter

SPEC will substitute $1 by peter and then it will try to execute

print peter

and because the variable peter has never been set you will get

100.SPEC> hello peter
0

The correct way to write the macro would be

def hello '{
print "$1"
}'

that will then give after substitution

print "peter"

Be aware of the side effects of this method of substitution/evaluation that may sometimes generate errors of this kind but you can also profit of it to obtain interesting results. In the exercises at the end of this chapter both, problems and tricks, are illustrated with examples.

Another important hint about string parameters: If you give strings in the command line surrounded by quotes SPEC always ignores the quotes so the piece of macro

def myprint '{
print $1
}'

will never print a string constant even if it is given with quotes in the invocation to the macro:

102.SPEC> myprint "Hello"
0

All this can be resumed as: if you want to use string parameters within a macro the $x needs always to be substituted by using quotes: "$x".

Parameters to macro functions

Parameters to macro functions are given inside brackets and separated with commas; the name for the variables to hold the parameters are declared with the macro definition and used as such inside the macro.

def speed(distance, elapsedt) '{
local average
print "This macro calculates average speed"
average = distance / ( elapsedt / 60 )
print "Average speed: " average
}'

108.SPEC> speed(12.7,26.5)
This macro calculates average speed
Average speed: 28.7547

The parameters are assigned to the variables at the moment the macro is called and not substituted as with traditional macros.

Getting output from macros

A common way to get output values out of a traditional macro is to assign one or several global variables with the values to be returned. Then this values can be used by other macros.

Macro functions can directly return a value and then assigned or used directly in a more compact way.

def speed(distance, elapsedt) '{
local average
average = distance / ( elapsedt / 60 )
return(average)
}'

109.SPEC> print speed ( 12.7, 26.5)
28.7547

Basic utilities to work with macros

A number of utility commands exist in SPEC to work with macros. Some of them are described here because of their particular interest. At ESRF we have developed a few others that can be found below in a separate paragraph.

Print macro definition: prdef

You can print the actual definition of a macro by typing:

prdef macroname

you could get the definition of several macros with one single command:

prdef macro-list

macro-list is the list of macros you want to print or a wild-card specification.

Before the actual macro definition prdef adds a comment line indicating where the macro has been defined from: tty if it has been defined by typing from the keyboard, or a file name if the definition was loaded from a file (see below to see how to load macro definitions from files)

110.SPEC> prdef speed
# tty
def speed(distance, elapsedt) '{
local average
average = distance / ( elapsedt / 60 )
}'

Listing macros: lsdef

to get a list of macros containing a certain pattern in their name or just to verify that a particular macro exists in your SPEC application you can use:

lsdef macro-list

For example, you can get the list of all the existing macros starting with k2001 ( Keithley 2001 multimeter) by typing:

111.SPEC>lsdef k2001*
k2001_close (36) k2001_postcount (23) k2001on (1209)
k2001_getcounts (402) k2001_precount (52) k2001setup (2167)
k2001_init (75) k2001body (168)
k2001_open (33) k2001off (303)

The number in brackets at the right of each macro name indicates the text length of the macro.

Loading macros from a file: qdo

You can write macro definitions into a file in the same way as you would type them in SPEC. A file may contain several macro definitions and lines with comments. Once the file has been saved it can be loaded into a SPEC application with the command qdo.

112.SPEC> qdo /users/a/opid02/macros/bicycle_stats.mac
Opened command file "/users/a/opid02/macro/bicycle_stats.mac" at level 1.

In this way it is possible to create libraries of macros and load them together. This is the way ESRF macro distribution is organised.

More utilities

There exist some other utilities you can use to work with macros, some are distributed with the standard SPEC distribution, some have been developed at ESRF.

  • emac

    emac is an ESRF written macro that allows you to edit a macro on the fly. You can select the editor to use with the SPEC variable EDITOR. Then when you call emac

    emac macro-name

    the selected EDITOR is called with the actual definition of the macro you want to edit. When you have finished editing the new definition is loaded into SPEC.

    The changes you have made with emac are immediately effective but they are not saved into disk; they will be lost if you start your SPEC application from fresh or if one of the macro files that are automatically loaded when you start SPEC defines that same macro.

  • savmac

    savmac is a standard SPEC macro that allows to save a macro definition into a file.

    savmac macro-name file-name

    The file is created if it does not exist, otherwise the macro definition is appended at the end of the existing file.

  • undef

    This built-in keyword allows you to remove a macro definition from SPEC memory. The syntax is:

    undef macro-name
  • jdo

    jdo is a short-cut for qdo to load macros from the directory SPECD/../jmacros where you can found the official ESRF macro distribution together with beamline specific macros. In this way if the variable SPECD is set to /users/a/specadm, typing:

    132.SPEC> jdo k2001

    is equivalent to typing qdo /users/a/specadm/jmacros/k2001.mac

  • unix commands

    if you are creating macro files directly from the SPEC prompt with emac, savmac... you may need to use unix commands like cd, ls and the like. You can send any UNIX command with

    unix("command")

    or with the short-cut:

    u "command"

    You can find some macros in SPEC to implement some of the most frequent ones: ls, pwd, cd, vi are also SPEC macros that do exactly what you would expect the UNIX command to do.

Automatic loading of macros at ESRF

This paragraph is intended for those readers who are involved in administering SPEC macro automatic loading. It explains the way the automatic loading of macros is organized at ESRF. If you are only interested in learning how to use SPEC and how to develop your own macros you may skip this.The full mechanism of how SPEC startup is not explained here, but you can get a complete description in the chapter dedicated to SPEC administration.

As you may know by now, at ESRF, and in fact in most SPEC installations, the SPECD environment variable points to a directory structure where you can find startup macros, config files and userfiles for every SPEC application. This environment variable has to be set before starting your SPEC application.

At ESRF, SPECD always points to a directory with name spec.d under the specadm user home directory. The specadm home directory contains also a directory called jmacros ( $SPECD/../jmacros ) from where SPEC automatically loads macros at startup. To select a loading sequence you need to set as well the environment variable SPECBL. This two variables will set the name of the first macro file to be loaded:

$(SPECD)/../jmacros/$(SPECBL)setup.mac

For example if SPECD is set to /users/a/specadm/spec.d and SPECBL is set to id3 the first file to be loaded will be /users/a/specadm/jmacros/id3setup.mac. If the SPECBL variable is not set, SPEC will prompt the user for it.

This file is executed every time SPEC is started and should contain the right information to load all the other macro files or other commands necessary at startup. In most cases this file is just a list of lines like jtdo(macrofilename).

jtdo is a macro function that will load from the directory jmacros the file with the name indicated in its argument (or completed with the suffix .mac) if the file has been modified since last time that SPEC application was started, not loaded if the file has not been changed since. If the SPEC application is started from fresh the files are always loaded.

In this way if you want a file to be included in this sequence:

  • the file has to be copied to the directory $SPECD/../jmacros
  • you need to add a line jtdo("yourfile") in $(SPECBL)setup.mac

You could automatically load files from another directories by including qdo lines on the file $(SPECBL)setup.mac. In that case the modification time will not be checked. Our recommendation is to use the directory jmacros to keep all macros necessary for SPEC.

Advanced Topics

SPEC internals: macro expansion

The SPEC interface to users is a command line interpreter: commands are interpreted and executed as they are entered at the application prompt. Traditionally, macros in SPEC are substituted by the interpreter until there is no more substitution to be done, then executed. This mechanism is used in all what we have called all along this chapter traditional macros. This means that if for example, macro A call macro B and then macro B call macro C, SPEC will put macro A into a buffer, the call to macro B will be substituted with its corresponding code inside the buffer and then macro C will be as well expanded. Only after that execution will be done.

Parameters to macros are as well substituted before execution as it has been discussed before in this chapter.

You need to well understand that mechanism to prevent side effects or to take profit of them. The first obvious thing that comes from this is that recursive macros are not possible in SPEC. The interpreter would get in an infinite expansion loop. If you try the macro

def factorial ‘{
local next
if ( accum == 0 ) accum = 1
next = $1 - 1
if ( next != 0 ) {
factorial next
accum = $1 * accum
}

p $1"\! = " accum

you get a stack overflow that means that the expansion buffer has been completely filled up:

133.SPEC> factorial 5
yacc stack overflow on "EOF".

but it general what this means is that you should be careful about doing big macros that call other macros with deep nesting. This may result in a neat slow down of the execution of your macro.

You must as well be aware of the way the parameters appear in the macro after substitution. Look at this example:

def trick '{
$2 = $1 * $1
}

134.SPEC> trick 4 square

135.SPEC> p square
16

In the example the macro trick is used to assigned a variable whose name is given in the command line. After substitution the line, $2 = $1 * $1, becomes: square = 4 * 4, that is a plain variable assignment.

As another, more sophisticated example, you can look at the macros in Appendix A: the macro blmenu is built up of three macros: blmenuhead, blmenubody and blmenutail. A for loop is started in blmenuhead and only closed in blmenutail. This does not produce a syntax error because the for loop is well completed after expansion.

Syntax checking of macros is done at expansion. For this reason you may get a syntax errors on a macro because of pieces of code hidden in the expansion and that you may not think of.

Macro functions are available in SPEC from version 4 onwards. Above in this chapter we discussed the formal differences between traditional macros and macro functions: the syntax, the way to pass parameters and the possibility for macro functions to return a value. Internally, there is a fundamental difference between both. Macro functions are not expanded before execution. They are executed and expanded separately only at the moment they are executed. In this way some of the drawbacks of traditional macros disappear, but some of the features disappear with them.

Now you can rewrite factorial as a recursive macro function:

def factorial(num) ‘{
local next
if (accum == 0) accum = 1
next = num - 1
if ( next != 0 ) {
factorial(next)
accum = num * accum
}
p num"\! = " accum

Other ways of manipulating macros: rdef, cdef

SPEC provides two other ways of changing a macro definition: rdef and cdef. rdef offers the possibility of defining or changing a macro from another macro. cdef builds a macro definition from pieces and keeps a table with those pieces that can be updated and listed.

  • rdef

    The syntax of rdef is

    rdef macro_name string

    In this way you can define or redefined macros inside another macro. Both traditional macros or macro functions can be redefined in this way. string can be either a constant string surrounded by quotes or a variable containing a string.

    def virus ‘'
    local secret
    secret = getval("Enter secret code","")
    if (secret == "petroff") {
    print "Okay...counting down for destruction."
    for (i=10;i>=0;i--) {
    printf("%2d\r",i)
    sleep(1)
    }
    rdef factorial "print \"Brrr.....\""
    print "Done"
    }
    '

    ‘

    This illustrates once more the mechanism of macro expansion. If you use rdef inside a macro to redefine a traditional macro, and later in the first macro you call the macro so redefined the result is not guaranteed. Because the expansion is done before the rdef is executed the new definition will only be used in the next expansion. In the other hand if you redefine a macro function with rdef the new definition is always available at the next call to the redefined macro because its expansion is done at the moment it is invoked.

  • cdef

    The function cdef() is use in some particular cases to define "chained" macros. A macro is made up of pieces, each of them identified with a key.

    For example, in most of the macros for moving, counting and scanning there exist calls to user_* macros at some strategic points. These macros, initially empty, can be then built up to introduce actions before or after a movement, at each scan point, after counting is done and in a bunch of other cases. This is the method that is used at ESRF to define logical motors and counters, for example.

    The syntax is:

    cdef("name", s, [key, [flags]])

    Where name is the name of the macro to build up and s is the string to be added to the macro definition. key is a handle useful to add or delete just one the pieces that build the macro. Remark that only one piece of macro can be added with the same key. If you add a second piece with the same key this will replace the first one.

The chained macro can have three parts: a front, a middle and back. Pieces included in each of the parts of the macros are sorted lexicographically by the keys. Pieces without a key are placed in the middle, in the order in which they were added, but after any middle pieces that include a key. With the optional key argument, the pieces can be selectively replaced or deleted. The flags argument controls whether the pieces are added to the front or to the back of the macro or whether the pieces should be selectively included in the definition based on whether key is a currently configured motor or counter mnemonic. The bit meanings for flags are as follows:

  • 0x01 - only include if key is a motor mnemonic and the motor is not disabled.
  • 0x02 - only include if key is a counter mnemonic and the counter is not disabled.
  • 0x10 - place in the front part of the macro.
  • 0x20 - place in the back part of the macro.

If flag is the string "delete", the piece associated with key is deleted from the named macro, or if the name is the null string, from all the chained macros. If key is the null string, the flags have no effect. The cdef() function will remove any existing macro defined using def or rdef. However, the commands lsdef, prdef and undef do work with chained macros. When SPEC starts, when the reconfig command is run (or the config macro is invoked) or when individual motors or counters are enabled or disabled, all the chained macros are adjusted for the currently configured and enabled motors and counters.

cdef("?")

Lists all the pieces of all the chained macros.

cdef(name, "", "?")

Lists the pieces of the macro named name.

Even if cdef() is a powerful function in many cases, overuse may lead to confusion due to the dynamic definition of the macro. As far as it possible static definition is recommended.

Appendix A - BLMENU.MAC

#%TITLE% BLMENU.MAC 
#%NAME%
# Macros to easily prepare a menu to access
# SPEC subsystems functionalities on a beamline.
#
#%DESCRIPTION%
# This macro set allows you:
# %UL%
# %LI% To change the state on a subsystem chosen from a list (main menu)
# %LI% To call a macro to see definitions, state or other interesting
# functionality on a subsystem chosen from a list (extra menu)
# %XUL%
#
# To get this functionality the macro has to be adapted to your beamline.
# You or your spec administrator must:
#%BR%%BR%
#
# %DL%
# %DD%- Write a macro function like for instance %B%mcabody(mode)%B%
# that accept a mode (0,1,2). Mode 0 should return a string with
# the status of the system that will be shown in blmenu. Mode 1
# would do the necessary to change the state of the system. Mode 2
# may call any macro to access other functionality in the system
# %DD%- A call to %B%blmenuadd%B% will add a line to blmenu. blmenuadd
# defines the necessary labels and macro name
# %XDL%
#
#%BR% %BR%
# The user will have to call a single macro \"blmenu\" and choose the
# subsystem from the list to change the state. Or select the option \"e\"
# to get a second menu with extra functionality
#
#%EXAMPLE%
#
# %DL%
# %DT% blmenu %DD% Starts the program
# %DT% blmenuadd("MCA","MCA setup","mcabody","mca")
# %DD% Will add a line to blmenu with the string \"MCA\" for main menu
# and \"MCA setup\" for extra menu. The macro that will be called in
# all cases is \"mcabody\" (with mode = 1 from main menu and mode=2
# from extra menu) and the key for handling definitions is \"mca\".
#
# %DT% blmenuadd("Keithley 2001","NONE","k2001body","k2001",0x02)
# %DD% Same as before but includes as well the flag \"0x02\". As result the
# line will be added if and only if %B%k2001%B% is defined as a counter in config.
# %XDL%
# See \"help funcs\" and look in the %B%cdef%B% paragraph for the meaning
# of other possible flags.
#

#%UU% ( label, extra-label, macro, key [,flag] )
#%MDESC%
# Add lines to blmenu. This macro should be called from a setup
# file. Lines will be deleted automatically when taken out from
# setup
global BL_PHASE
global BL_MAIN BL_SHOWEX BL_EXTRA

BL_MAIN=0
BL_SHOWEX=1
BL_EXTRA=2

def blmenuadd(label,elabel,macro,key,flag) '{
local nok_flag
nok_flag=0
#
# Checking
# - that there is a key present
# - that the macro exists
#
if (whatis("key")&0x8000000) {
p "No key present in blmenuadd"
nok_flag=1
}

if ((whatis(macro)) == 0) {
printf("Macro \"%s\" is not a macro name (blmenuadd)\n",macro)
nok_flag=1
}

if (!nok_flag) {
cdef("blmenubody",sprintf("blmenuoption \"%s\" \"%s\" %s\n",label,elabel,macro),key,flag)
setup_tail("blmenu",key)
}
}'

#%IU%
#%MDESC%
def blmenuunsetup '{
cdef("blmenubody","","$1","delete")
}'

#%UU%
#%MDESC%
# Will start the \"blmenu\" program
def blmenu '
blmenuhead
blmenubody
blmenutail
'

#%IU%
#%MDESC%
# This is the title for blmenu. Do not need any change
#
def blmenuhead '
#
# the bracket is closed in blmenutail
for (;;) {
local option ite yes_e state

ite=0
clscreen()
if (BL_PHASE==BL_SHOWEX || BL_PHASE==BL_EXTRA)
prtitle(SPECBL,"Instrument Menu (EXTRA)")
else
prtitle(SPECBL,"Instrument Menu")
'

#%IU%
#%MDESC%
#
def blmenuoption '

if (BL_PHASE==BL_EXTRA || BL_PHASE == BL_SHOWEX) {
if ("$2" != "") {
ite++
}
} else {
ite++
}

if (option == ite) {

if (BL_PHASE==BL_EXTRA) {
clscreen()
$3(2)
input("\n\nPress return to continue.")
state = $3(0)
option = -1
continue
}

if (BL_PHASE==BL_MAIN) {
state = $3(1)
}
}
else state = $3(0)

if (BL_PHASE==BL_SHOWEX || BL_PHASE==BL_EXTRA) {
if ("$2" != "") {
state=""
proption "$2" ite state
}
} else {
proption "$1" ite state
}
'

#%IU%
#%MDESC%
#
def blmenutail '

if ( BL_PHASE != BL_MAIN) {
BL_PHASE = BL_EXTRA
option = getval("\n\tEnter option number or 0 to return to main",0)
if (option < 1 || option > ite ) { BL_PHASE = BL_MAIN }
} else {
option = getval("\n\tEnter number to change, e for extra, or 0 to exit",0)
yes_e = index(option,"e")
if (yes_e) {
BL_PHASE = BL_SHOWEX
} else {
if (option < 1 || option > ite ) break
}
}

#
# This close the bracket opened in blmenuhead
}

'



#%IU%
#%MDESC%
# Prints the title on the menu.
def prtitle(bl,title) '{
printf("\t\t")
tty_cntl("mr"); tty_cntl("md"); printf(" %s - %s ",bl,title); tty_cntl("me")
printf("\n\n")
}'

#%IU%
#%MDESC%
# Prints one option line
def proption '{
tty_cntl("md"); printf("%02d",$2); tty_cntl("me")
printf(" %26s ","$1")
tty_cntl("md"); printf("%s",$3); tty_cntl("me")
printf(" \n")
}'

#%MACROS%
#%IMACROS%
#%AUTHOR%
# BLMENU.MAC - ICNTL - V.Rey 15/10/96.
#%TOC%

This example illustrates many of the subjects discussed in this chapter.

blmenu is a macro used at ESRF beamlines to change the state of a subsystem from a SPEC point of view, and to save the user from learning a considerable amount of macro names.

When a new group of macros is added to the application the programmer can add a line to blmenu by using the macro blmenuadd and defining a macro function that will be called from within blmenu. Extra macros can be executed from a second menu. The state of blmenu is reflected in the variable BL_PHASE that is updated depending if the program is showing the main menu, extra menu or executing one of the extra macros.

If you look in the code you will notice the large number of comment lines "#" that contains strange keywords. These lines are used later for automatic documentation and are not explained here. Because they are comment lines are just ignored by SPEC.

Notice how blmenubody is made up of pieces by using the cdef function. These pieces are calls to blmenuoption with the parameters entered in blmenuadd.

You can see examples of the use of macro expansion at different places. In particular the macro blmenu itself is made up of three other macros: blmenuhead, blmenubody, blmenutail. blmenuhead and blmenutail make no sense as individual macros and they would give a syntax error if we try to execute them separately. blmenuhead opens a for loop that is only closed in blmenutail. The expansion allows to be completed at the moment of the execution.

Another example is the way the macro supplied for each subsystem is called from within blmenubody. The name of the macro is one of the parameters, $3, that is used as such. The macro expansion will put the right macro name at each call to blmenubody.

prclear,prtitle and proption are just used to format the text on the screen.

Questions and Exercises

  1. Write a macro that calculates the maximum of two numbers first as a traditional macro then like a macro function.
  2. What happens if a standard macro uses parameter $4, but the user only types 3 parameters. How can you check if a parameter has been provided for a macro function. Make this work for strings and numbers.
  3. What happens if you do not use any parameter in a macro, but the user types a parameter. How can you explain this behaviour. Why could somebody have included the strange first line in the following macro:
    def get_it '
    #$#
    for (i=0;i<10;i++) {
    print A[i]
    } '
  4. Try to define a recursive function with macro functions. Discuss the performance issues related with recursive functions.
  5. Use cdef to write a macro that will inform which of the three motors called del, ome and th are configured in your SPEC application.
  6. Look at the following macros and guess what will be printed in the screen after execution. How do you explain that?

    def weather '{
    print "Sunny"
    }'

    def forecast '{
    if ( $# != 1) {
    print "Usage: forecast month"
    exit
    }

    if ( "$1" == "January" ) {
    rdef weather \'{
    print "Cold"
    }\'
    }

    if ("$1" == "March" ) {
    rdef weather \'{
    print "Rainy"
    }\'
    }

    weather
    }'

    152.SPEC> forecast January

    cdef is used here to add and delete pieces from the macro blmenubody.

    $3 will be substituted with the name of a macro, that is passed as an argument. Then executed. This is an example of how to get profit out of the mechanism of macro expansion.


European Synchrotron Radiation Facility