BLISS: Spec Tutorial CH3 The SPEC Language
Chapter 3 of the BLISS Spec tutorial
The SPEC language
SPEC has its own built in language. In the previous sections you saw already some simple examples of SPEC programs. You will soon come to a point where a simple sequential execution of commands is not longer sufficient. This section will introduce you to the SPEC language. The language is SPEC specific but resembles the language C enough to simplify the learning for those familiar with C.
Variables
We start our language excursion with the different types of variables and how to use them. To assign a value to a variable you will use very often the form:
variable = expression
For example one could type:
99.SPEC> temperature = 35.4
100.SPEC> print temperature
35.4
This example seems to be very simple, but a closer look will show us two important features of SPEC variables:
- We did not need to declare the variable "temperature". It got automatically created the first time when we used it.
- The variable does not have a specific type. You can assign an integer value, a floating point value or a string to the variable without any problem.
temperature = "too hot"
temperature = 35
When talking about variables we will have to know where this variables are valid (the scope of the variable). There are global variables which can be accessed and set everywhere and local variables which are only used in one block.
We will also have to distinguish between ordinary variables and arrays. Arrays are used to store more than one value and can be accessed with
arrayname[index]
Scope
Global Variables
Global variables are global to the whole SPEC session. You can quit SPEC and restart it and a global variable will still keep its value. A global variable can be accessed and set everywhere. I try to restrict my usage of global variables with the following rules:
- Use global variables only when necessary.
- Name a global variable with capital letters only (like MCA_CHANNEL). This makes it much easier to identify a global variable in a macro.
- start all the global variables in a macro set with the same letter combinations. (like MCA_UNIT, MCA_CHANNEL, MCA_DEVICE.). This makes it much more unlikely to use the same name twice. It makes it also simpler to list all global variables from a macro set.
Local Variable
Local variables are used to store values which are only needed inside a statement block. A statement block is any number of instructions enclosed in curly brackets.
It is a common error to think about local variables in connection with macros. Local variables are not local to a macro definition, but local to a statement block enclosed in curly brackets.
Creating Variables
You can either explicitly declare a variable before usage or just assign a value to it. You can use the following syntax to declare variable- local variable_name1 variable_name2. or
- local variable_name1, variable_name2.
- global variable_name1 variable name2. or
- global variable_name1, variable_name2.
If you assign a value to variable which has not been declared, SPEC automatically declares it for you. SPEC will guess, if the variable should be local or global. If the variable is used inside a statement block in curly brackets it will be local to this statement block. If the assignment is not enclosed in curly brackets, SPEC will create a global variable.
These statements all create the global variable ID_DEVICE:
68.SPEC> ID_DEVICE="fe/id/11" or
69.SPEC> global ID_DEVICE or
70.SPEC> global ID_UNIT ID_DEVICE ID_NAME
These statements will all create a local variable called "i":
def count_up '{
for (i=0; i<10; i++)
print i
}'
74.SPEC> count_up or
75.SPEC> {local i ; i = 7 }
The global or local statement
will not erase the contents of the variable if it already exists. It is
therefore safe to declare all the global variables used in a macro in
the beginning of the macro as illustrated in the following example
def mirrorsetup '
global MIRROR_MNAME MIRROR_OLDPOS MIRROR_LEN
MIRROR_MNAME = "$1" # Motor1
MIRROR_LEN = $2
mirror_pdef
'
Constant variables
We should better say "constant symbols", but constants are just like global variables with the exception that you can not assign values to them in the normal way. The syntax to create a constant and assign is a value is:
constant name value_expression
For example you could define PI2 as twice the value PI in the following way:
280.SPEC> constant PI2 2 * 3.1415926
If you tried now to assign a value to it in the normal way, SPEC would give you the following error message
281.SPEC> PI2 = 4.5
Trying to assign to a constant "PI2".
Exercises
In the following cases, what will be the output written to the screen:
def ex_1 '{
local i ;
i = 5
{
local i
i = 3
}
print i
}'
def ex_2 '{
local i ;
i = 5
{
i = 3
}
print i
}'
def ex_3 '
local i ;
{
i = 120
print i }
print i
'
def ex_4 '
local i ;
i = 7
print i
'
def ex_5 '{
global i
{local i ; i = 12}
print i
}'
120.SPEC>ex_1 ; ex_2 ; ex_3 ; ex_4 ; ex_5
Can you explain why? Be especially careful with the output of ex_5.
Some utilities connected to variables
syms
The syms command lists all the symbols currently in use.
syms [pattern_name ..]
This can be useful to see if a certain variable name is already in use or if you can not remember the exact name of a global variable.
36.SPEC> syms S*
10320 S (BA...) 80 SLIT_H (G.NS.)
80 SCAN_D (G....) 80 SLIT_N (G.N.C)
80 SCAN_N (G.NS.) 80 SLIT_W (G.NS.)
96 SEDITOR (G..S.) 96 SPEC (B.SI)
80 SETUP (G.N..) 96 SPECBL (G..S.)
96 SHMID_image_data (B.N.I) 112 SPECBL_DEF (G..SC)
96 SHMID_test1 (B.N.I) 112 SPECD (B..SI)
96 SHMID_test2 (B.N.I) 96 STARTUP_ERR (G.N..)
96 SHOW_SU_ERR (G.N.C) 96 STLOCAL (G.NS.)
Whats and WHATIS
The whats command prints additional information about the symbol.
33.SPEC> whats BG
A global variable with number attributes.
35.SPEC> whats nothing
Not a macro, keyword, function or variable.
You can use this information inside your macro with the whatis function. The whatis function takes a string as an arguments and gives a number back, where the information about the string argument is coded.
whatis(s, ["info"] )
The number is a two-word (32-bit) integer, with the low word containing a code for the type of object and the high word containing more information for certain objects.
|
TABLE 1. |
||
|
High Word |
Low Word |
Meaning |
|
0 |
0 |
Not a command, macro or keyword. |
|
0 |
1 |
Command or keyword. |
|
Length |
2 |
Macro name (length is in bytes). |
|
0x0001 |
4 |
New-style data array. |
|
0x0010 |
4 |
Number-valued symbol. |
|
0x0020 |
4 |
String-valued symbol. |
|
0x0040 |
4 |
Constant-valued symbol. |
|
0x0100 |
4 |
Associative array. |
|
0x0200 |
4 |
Built-in symbol. |
|
0x0400 |
4 |
Global symbol. |
|
0x0800 |
4 |
Unset symbol. |
|
0x2000 |
4 |
Immutable symbol. |
|
0x4000 |
4 |
Local symbol. |
You can use this information to create a macro only if it is not defined yet:
if (! (whatis("user_finished")&2) )
rdef user_finished ""
This checks to see if user_finished is already a macro by checking the second bit in the return value from whatis. If it is not defined yet it will define an empty macro with the name user_finished.
unglobal
The command to delete a global variable take the following format:
unglobal variable_name1 variable_name2 ...
Types
SPEC variables can not be declared to have a specific type (like integer, float, double, strings, arrays). When you use variables, you normally do not have to worry about their type. SPEC will internally keep track of the type of the variable. This section will try to give you a profound understanding how SPEC handles internally the type of a variable. This will help you to sort out eventual problems, but is maybe not necessary on first reading.
Internally a variable will have a string value and a numeric (double precision floating point) value. SPEC uses the one appropriate. In arithmetic operation it will use the numeric value and in string operations the string value. All this is done completely automatic. The numerical value of a string which can not be interpreted as a number is 0 (zero).
Let's go through an example:
1.SPEC> global TEMPERATURE
2.SPEC> whats TEMPERATURE
A global variable that has not been set.
3.SPEC> p TEMPERATURE
0
We created a global variable TEMPERATURE. You could see that SPEC knows that the variables has not been assigned a value yet. The value of such an unset variable is 0 (zero).
5.SPEC> TEMPERATURE = 35
6.SPEC> whats TEMPERATURE
A global variable with number attributes.
7.SPEC> p TEMPERATURE , TEMPERATURE + 1
35 36
We assigned a numerical value to the variable. This made the variable numerical. We could use it correctly in the arithmetic expression "TEMPERATURE + 1"
8.SPEC> TEMPERATURE = "TOO HOT"
9.SPEC> whats TEMPERATURE
A global variable with string attributes.
10.SPEC> p TEMPERATURE , TEMPERATURE + 1
TOO HOT 1
We assigned a string value to the variable and it became a string variable. As you could see its numerical representation is 0.
11.SPEC> p TEMPERATURE[1]
Illegal reference to non-array "TEMPERATURE".
We tried to use the variable as an array (more about arrays later) and SPEC produced an error. You can use a variable either as an array or as a normal variable but not both.
Arrays
SPEC has two types of arrays. There are associative arrays which have been created to be simple to use and there are data arrays which have been introduced to hold big amount of data and access it as fast as possible. Let's first have a short look in both type of arrays and talk about the differences later.
Associative arrays
SPEC arrays have a name and one or two indices. (For one or two dimensional arrays). They are accessed in the following way:
name [index]
or
name [index][index]
As the other SPEC variables, SPEC associative arrays do not have an explicit defined type. This is an example how to put string variables in an array and access them from a loop.
28.SPEC> animal[0] = "cat"
29.SPEC> animal[1] = "dog"
33.SPEC> animal[2] = "horse"
34.SPEC> for (i=0;i<3;i++)
35.more> p animal[i]
cat
dog
horse
An interesting feature of associative arrays is that there indices do not have to be in consecutive order. In the following example only one element with index "100000" is created. (i.e. only the storage place for one element is needed)
46.SPEC> g[100000]=1
The indices to associative arrays can be strings. This can be useful to associate a string to another string.
47.SPEC> tool["hammer"]="nail"
48.SPEC> tool["screwdriver"]="screw"
49.SPEC> p tool["hammer"]
nail
Associative arrays can have two dimensions. (Currently two is the limit of dimension for SPEC arrays.)
51.SPEC> room["bath"][1] = 45000
52.SPEC> room["bath"][2] = 50000
53.SPEC> room["bedroom"][1] = 30000
There is a special for loop in SPEC to loop over all elements of associative arrays. It will be discussed again later, but it might be useful now to give some examples. Be careful when using this loop, as the order of the elements is not defined.
60.SPEC> for (idx in tool) {
61.more> p tool[idx]
62.more> }
screw
nail
63.SPEC> for (i in room["bath"]) {
64.more> p i, room["bath"][i]
65.more> }
2 50000
1 45000
You can also loop over the elements of a two dimensional array in one single loop.
74.SPEC> for (i in room) {
75.more> p room[i]
76.more> }
30000
50000
45000
Testing if an array has an a certain index.
77.SPEC> def print_el '
78.quot> if ("$1" in tool) {
79.quot> print tool["$1"]
80.quot> } else {
81.quot> print "empty"
82.quot> } '
83.SPEC> print_el hammer
nail
84.SPEC> print_el toothbrush
empty
Individual array elements can be deleted with the delete command. Its syntax is:
delete variable_name
These commands delete the elements from memory.
delete tool["hammer"]
delete room["bath"][2]
Data arrays
Associative arrays are very flexible and simple to use. If it comes to data storage, these arrays show some serious drawbacks. To use an associative array to store the data for a 1000 by 1000 pixel ccd camera would consume a lot of space and would be very slow. Data arrays are used to store data of a certain specific type only and have to be pre-declared. Data arrays are explained in detail in the chapter "Data handling in SPEC". At this point we will only present some simple examples:
We declare a global data array "mydata" with space for 10 double precision floating point numbers.
38.SPEC> double array mydata[10]
The data array mydata can be
accessed now in the same way as associative arrays. If used in this way
you will not see any difference between the different SPEC arrays.
39.SPEC> mydata[0]=0
40.SPEC> mydata[1]=1
41.SPEC> mydata[2]=4
42.SPEC> mydata[3]=9
44.SPEC> for (i=0;i<4;i++)
45.more> p mydata[i]
0
1
4
9
But on the contrary to the associative arrays, you can carry out mathematical operations for data arrays on all or selected array elements with one simple expression.
46.SPEC> mydata = sqrt(mydata)
47.SPEC> for (i=0;i<4;i++)
48.more> p mydata[i]
0
1
2
3
There are powerful functions to handle these data arrays (like plotting or fitting) and more can be added with external programs. This is the reason why we dedicated an extra chapter to data handling in SPEC. See there for more.
Advanced
The following example shows a macro which will create the array image_data only if it has not been created yet or if its size has changed. The new size is calculated and stored in the variables xsize and ysize and the old size if stored in the variable oldx and oldy. See the usage of whatis to find out if image_data exists already. The Flag tested corresponds to the global symbol flag. If you can answer the question why you have to check for this flag and not the "is a variable?" symbol, you reached true proficiency in SPEC.
# SPECD/../jmacros/ccd.mac
def ccd_createarray '{
local xsize ysize oldx oldy
xsize = image_par(CCD_U,"row_end") - image_par(CCD_U,"row_beg") + 1
ysize = image_par(CCD_U,"col_end") - image_par(CCD_U,"col_beg") + 1
# Ask if image_data is global. If I use array_op on image_data it will
# create a local symbol image_data and whatis will not work.
if (whatis("image_data") & 0x4000000) {
oldx = array_op("rows",image_data)
oldy = array_op("cols",image_data)
}
if ( oldx != xsize || oldy != ysize ) {
shared ushort array image_data[xsize][ysize]
}
}'
Expressions
Most people are familiar how to define expressions in a programming languages. This paragraph will therefore only give a list of the different operators and give some example how to use the less common ones. If you would like to have a better introduction to programming, we recommand to read a book about C (like the classic from Kernigham and Ritchie: Programming in C).
Arithmetic Operators +, -, *, /, %
The % operator computes the reminder when the first operand is divided by the second.
293.SPEC> p 12 % 5
2
Comparision Operators >, >=, <, <=, ==, !=, !, &&, ||
The comparison operators result in 1 (which corresponds to TRUE) or 0 (which correspond to FALSE) in SPEC.
297.SPEC> p (4 == 2 + 2)
1
They will be normally be used to formulate conditions in a SPEC macro. The following code could be part of a macro to test that the macro is not called with more than 5 parameters.
if ($# > 5) {
print "Sorry - too many parameters"
exit
}
The operators >, <, >=, <=, ==, != work with numerical and alphanumerical operands.The expression x > y for example will result in TRUE if both x and y are numerical and x is greater than y. It will also result in TRUE if both variables are string variables and the x is after y in alphabetical order. Some examples:
394.SPEC> p 4 > 5
0
395.SPEC> print 4 > 5
0
396.SPEC> print "afternoon" <= "morning"
1
397.SPEC> print "Hello" == "hello"
0
398.SPEC> p 4.0 == 4
1
Be careful to distinguish between the assignment operator "=" and the equal operator "==" , if you are not used to program in C. This is specially dangerous as using the assignment operator often results in a valid expression (no syntax error!)
300.SPEC> x = 45
301.SPEC> if (x == 10)
302.more> print "x is 10"
303.SPEC> if (x = 10)
304.more> print "x is 10"
x is 10
305.SPEC> p x
10
The negation operator ! results in 0 if its operand is not 0 and in 1 if its operand is 0.
if (! (x > 100))
is equivalent to
if (x <= 100)
The exclamation mark has a special meaning when typed in on the command line of SPEC. If ! is followed directly by a number (like !23), it will replace this with line number n you typed before (!23 will be replaced with line 23 from your command line history. !string will be replaced with the last command line which started with string and !! is the last line typed in. If you are interested in these features you can read about it in "help history". At the moment we would like to use the ! in our expressions when typed in on the command line. There are to possibilities to do that. We can type a blank after the ! or escape the ! by typing a \ before. These special considerations are only important when you use the exclamation mark directly on the command line. If you define a macro, you can use ! without any problems as it looses its special meaning.
311.SPEC> p ! (10 >20)
1
312.SPEC> p !(10 >20)
!(10: event not found
313.SPEC> p \!(x >20)
1
The && and || operators allow you to produce more complicated logical expressions. && is the logical and and results in 1 only when both of its operands are not equal 0. || is the locaical or and results in 1 with at least one of its operands being not equal to 0. To check for example if x is in the valid range between 20 and 40 you could use the conditional expression:
(x > 20 && x < 40)
If the return value of a function can be 5 or 6 to indicate an error you could write the following condition to check for errors:
(x == 5 || x == 6)
Bit Manipulations &, |, ^, ~, <<, >>
These operators work with numbers in their binary representation. Imagine the number as a series of 0 and 1.
The and (&) operator set the bits in the
resulting number only when both corresponding bits of its operands are
1. The or (|) operator sets the bit to 1 if one or the other operand's
bit are 1. The ^ is called the "exclusive or" operator and sets the
bits to 1in the result when either one or the other operand's bit are
set to 1. On the contrary to the or operator, the resulting bit will be
0 if boths bits of the operands are 1. The negation (~) operator sets
all the bits in an operand from 1 to 0 and from 0 to 1. Some examples
should make that clear:
x= 74 --> 0x4a --> 0100 1010
y=135 --> 0x87 --> 1000 0111
|
TABLE 2. |
|||||
|
|
or (|) |
and (&) |
excl. or (^) |
~x |
~y |
|
x |
0100 1010 |
0100 1010 |
0100 1010 |
0100 1010 |
|
|
y |
1000 0111 |
1000 0111 |
1000 0111 |
|
1000 0111 |
|
binary res. |
1100 1111 |
0000 0010 |
1100 1101 |
1011 0101 |
0111 1000 |
|
hex res. |
0xcf |
0x02 |
0xcd |
0xb5 |
0x78 |
|
decimal res. |
207 |
2 |
205 |
181 |
120 |
We can see if our calculations were right with SPEC:
323.SPEC> x= 0x4a
324.SPEC> y= 0x87
325.SPEC> p x | y , x & y, x ^ y, ~x, ~y
207 2 205 -75 -136
Everything seems to be OK except the last two results which are not what is written in the table. Why does the SPEC output for ~x read -75 and the number in our table is 181. The reason is that we made a wrong assumption when calculating ~x. We wrote the number down with only 8 bits and not 32 bits as stored in SPEC. SPEC will negate all the other bits in the number, too. Therefore we end up with the binary number 11111111111111111111111110110101. This number is interpreted as a negative number from SPEC and this leads to -75. (A number with all bits set to 1 is interpreted as -1). To arrive at the same result as in the table we have to mask off all the higher order bits. We can use the and-operator to do that.
338.SPEC> p ~x & 0xff
181
339.SPEC> p ~y & 0xff
120
Be careful to distinguish well between the bitwise and/or operators and the logical and/or operators from the last section. So results 1&2 in 0 but 1&&2 in 1.
The last two remaining operators >> and << are used to shift the bits in a number to the left (<<) or to the right (>>).
00010001 << 2 == 01000100
00010001 >> 2 == 00000100
You see that the bits in the number get shifted to the left or to the right. You remark that bits can fall off the edge and dissapear when they get shifted "outside" the number. The operation << 2 corresponds to a multiplication with 4 and the operation >> 2 to a division by 4.
Compound Assignments *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=, ++, --
SPEC can combine the assignment with one operation from the previous sections. The idea of such an compound assignment is to carry it out on the number itself. It can be understood by taking "a op= b" as equal to "a = a op b". So will the expression x *= 2 double the value in x for example. The expression variable++ or variable-- will add or substract one from the variable.
340.SPEC> x = 5
341.SPEC> x *= 2
342.SPEC> p x
10
343.SPEC> x -= 6
344.SPEC> p x
4
345.SPEC> x *= 3
346.SPEC> p x
12
347.SPEC> x++
348.SPEC> p x
13
The expression variable++ needs some more explanation as it can be written as variable++ or ++variable. The result will be in both cases that the variable is raised by one. The difference is the value which is returned by this expression. In the case of variable++ the value returned by the expression is the contents of variable and in the case ++variable the resulting value is variable+1. Another way of saying this is that in the case of ++variable the variable is raised before usage and in the case of variable++ it is raised after usage. An example will make that clear:
349.SPEC> x =10
350.SPEC> p x++
10
351.SPEC> p x
11
352.SPEC> x = 10
353.SPEC> p ++x
11
354.SPEC> p x
11
You saw that the effect on the variable was in both cases the same, it went from 10 to 11. In one case x++ was calculated to be 10 and in the other case ++x was calculated to be 11.
The same applies of course to the -- operator.
Conditional Expressions
The conditional expression is a simple way to make an expression depend on an extra condition. The syntax is:
expression1 ? expression2 : expression 3
The value of this construction is either expression2 or expression3 depending on expression1. If expression1 is not equal 0 (TRUE) then the value of the whole expression is expression2 else it is expression3. To get the maximum of two numbers you could write for example:
maximum = (x > y) ? x : y
SPEC special dereference operator
SPEC has a special dereference operator to access variables which name is only known when executing the program. @name_variable corresponds to the variable which name is stored in the variable name_variable. If the variable x contains the string "myvar", @x represents the variable myvar. This is a small example to illustrate that.
365.SPEC> y = 134
366.SPEC> x = "y"
367.SPEC> p @x
134
368.SPEC> @x = 200
369.SPEC> p y
200
The main usage of this feature is related to arrays. Let's suppose you would like to write a general background substraction function which will calculate the background of a spectrum and substract the calculated background from the spectrum. The name of the array which contains the spectrum should be stored in the global variable PLOT_ARRAY. The reason that the name of the array is stored in a global variable is that you would like to write different functions like plot, fit and so on which can be called by the user from SPECs command level. You have two choices now:
- You can either copy the contents of the array to temporary array and work all the time with the same temporary array. This will cost time and storage space.
- You can store the name of the array in a global variable and use the @PLOT_ARRAY construction to refer to the array.
If the current data is stored in the array SCAN_DATA, you could assign PLOT_ARRAY to "SCAN_DATA" with the following SPEC command:
PLOT_ARRAY = "SCAN_DATA"
When you would like to get the fist element of the second column of the SCAN_DATA array, you can write now:
first_y = @PLOT_ARRAY[0][1]
Precedence of SPEC operators
The following table lists the SPEC operators in the order of precedence. Everybody knows that multiplication takes precedence over addition. For example 3 * 4 + 1 has to be evaluated as (3 * 4) + 1. In the same way the more complicated operators can also be classified in the order of precedence. If you have doubts simply use parenthesis () to group expressions.
|
TABLE 3. |
|
|
operator |
meaning |
|
@ |
dereference operator |
|
name[index] |
array accessing |
|
++ -- |
increment, decrement in variable++ |
|
++ -- |
increment, decrement in ++variable |
|
~ |
bitwise not |
|
! |
logical not |
|
- |
arithmetic negation |
|
* / % |
multiplication division |
|
+ - |
addition substraction |
|
<< >> |
left or right shift |
|
< > <= >= |
relational operators |
|
== != |
equal or not equal |
|
& |
bitwise and |
|
^ |
bitwise exclusive or |
|
| |
bitwise or |
|
&& |
logical and |
|
|| |
logical or |
|
? : |
conditional |
|
= += -= *= /= %= <<= >>= &= ^= |= |
assignment and compound assignment |
Exercises
- Write a one-line macro function to return the minimum of two numbers.
- Is the following expression testing if x is in between 2 and 4 or do you need some extra parenthesis: if (x >= 2 && x <= 4)
-
What is the result of the following expressions:
1 << 3
2 >> 2
1 & 3
1 & 2
1 && 2 - Write one line to set bit number 2 in the variable x, one line to clear bit number 2 in the variable x and one line to check if bit number 2 is set in the variable x.
- Write an expression to test if the variable y can be divided by 5.
Flow control
Statements and Blocks
The end of the line or a semicolon makes a statement from a expression. If you are familiar with the C language you know that every statement ends in a semicolon there. In its general idea to make programming as simple as possible, this requirement had been released in SPEC. You already noticed this in previous chapters. Some example of a statement are:
y = 34 or
print Hello or
z = 45 ;
You see that a statement can be ended with a semicolon. This is useful to put multiple statements in one line. Like in
x[0]=0; x[1] = 2; x[2] = 4
If you want to continue a single statement over more than one line, you will have to escape the (invisible) end of line character by typing a backslash \ at the end of the line to be continued. Be careful that there is no other character (like a tab or a blank) after the backslash \. For example:
print "The numbers you asked are for are given her:", number1, \
number2, number3, number4
You can group multiple statements to a block with the use of curly brackets ({}). Such a block is equivalent to a single statement. This is used for example for conditional execution of the following statements:
if (x == 4) {
print "x is 4, this is not allowed"
exit
}
Without the use of the curly brackets the exit statement would be executed all the time. With curly brackets it is only executed when x is equal to 4.
Another way to group multiple statements to one statement is to separate the individual statements with a comma (,). The syntax is:
statement , statement , statement
The difference between separating statements with , or ; is shown in the following example:
422.SPEC> if (0)
423.more> x = "One Statement" , y = "Another one"
424.more>
424.SPEC> p x,y
0 0
425.SPEC> if (0)
426.more> x = "One Statement" ; y = "Another one"
427.SPEC> p x,y
0 Another one
The if (0) in the example will make sure that the following statement is not executed. As you can see, SPEC accepts the comma separated list as one statement (which is not executed in the example), but the semicolon separated list as two statements.
Sometimes the comma operator does not work correctly. This is when the statement before accepts , as possible input values. Like in:
428.SPEC> print "Hello" , print "World"
print "Hello" , print "World"
^
Syntax error on "print".
when the print commands interprets the comma.
The if-statement
The syntax for the conditional execution in SPEC is
if (expression)
statement1
else
statement2
Statement1 is executed if the expression is not equal 0 (TRUE) else the statement2 is executed. The else part is optional. The following is an example of a simple if statement.
if (n >= 0)
sign = 1
else
sign = -1
There is a two problem with the if statement which comes from the fact that the else statement is optional. If there are multiple if statements with some else missing it is not easy to see to which if statement an else belongs. This is easy to see in the following example:
if (n >= 0)
if (n == 0)
sign = 0
else
sign = 1
The problem is now to know to which if statement (the (n >=0) or the (n==0)) the else belongs to. SPEC solves this conflict by always using the if statement which is closest to the else and does not have another else statement yet. In our example the else statement belongs to therefore to the (n == 0) if statement as indicated by the indentation. (for help human readers). You can always use curly brackets if you have any doubts. In our example you could also have been written:
if (n >= 0) {
if (n == 0)
sign = 0
else
sign = 1
}
to make it really clear to which if statement the else statement belongs. An application of the else rule mentioned above is often used to formulate multiple if-else chains. For example:
if (x <= 0) {
print "No error"
} else if (x == 1) {
print "Could not open input file"
} else if (x ==2) {
print "Could not open output file"
} else {
print "Unknown error"
}
The following problem is less important. Look at the following example of typing
372.SPEC> if (x = 2)
373.more> p "x is two"
374.more>
x is two
The line you have to look to see the problem is 374. You see that SPEC did not write "x is two" immediatly to the screen, but waited for you to type the next line. (line 374 which is empty). The reason for this behaviour is that SPEC does not know if an else statement will follow.
The for loop
The standard loop to execute a statement multiple times is constructed with the for statement in the following way:
for (expression1; expression2; expression3)
statement
The statement will be executed as long as expression2 is TRUE (!= 0). Before the statement is executed for the first time expression1 is executed. expression3 is executed every time after the statement is executed. The standard way to execute a statement n times in SPEC is done with the following statement:
for (i = 0; i < n; i++)
statement
You see that before anything else is done i is set to 0 with the first expression. After that i is tested if it is smaller than n. If n is greater than 0, the statement is executed for the first time. After the execution of the statement, i is incremented to be 1. Then i is tested again to be smaller than n. If n is greater than 1, the statement is executed for the second time. In total the statement will be executed n times. At the end of the nth execution, i will be incremented to be n and fail the test. Therefore execution will continue with the next statement in the program.
The expressions do not all have to be present. For example, if the first expression is missing, there will be no initialisation. The following two statements do the same thing as the last example above:
i=0
for (;i<n;) {
statement
i++
}
In this way, an endless loop can be written as:
for (;;)
statement
The while loop
Another way to construct loops in SPEC is with the help of the while statement. Its syntax is:
while (expression)
statement
If the expression is different from 0 (TRUE), the statement is executed. The statement is executed as long as the expression is TRUE. The above for loop could be written also in the following way:
i=0
while (i <n) {
statement
i++
}
The for and while loops are the same as the ones defined in C. Contrary to C, the SPEC language does not include the do-while nor the switch statement.
The break statement
The break statement is used to leave a loop before its regular end. When SPEC encounters a break statement it leaves the innermost loop. In the following example, the break statement is used to leave the for loop if there is an error.
for (i = 0; i < 1000; i++) {
measure_pressure
if (PRESSURE_ERROR != 0 ) {
print "Error measuring pressure"
break
}
}
print "Continue with other important tasks"
The macro measure_pressure is supposed to measure a value and store it. In case of an error it could set the global variable PRESSURE_ERROR. The break statement inside the if, would leave the loop and continue with the print statement "Continue with other important tasks".
The continue statement
The continue statement is similar to the break statement in the sense that it also is used inside loop constructions. As soon as a continue statement is reached the current loop iteration is stopped and the next one is started. You can imagine that the continue statement jumps back to the beginning of the loop and starts the next iteration.
for (i=0; i< 1000; i++) {
if (data[i] < 0)
continue;
treat_data
}
In the above example, the macro treat_data will only be called for positive elements of the array data. If a negative array element is found, the continue statement is called. Therefore the next iteration is started. The counter i is raised.
Exercises
- Write a macro to print the first 10 non negative elements of the array "items".
- Write a macro to sort the 100 elements of the array "numbers".
- What will be the output of the following macros on the screen:
def loop_1 '{
for (i=0; i<10; i+= 2) {
print i
}
}'def loop_2 '{
local x
x = 34
while (x == 0) {
x = 43
print "X is now", x
}
}'def loop_3 '{
local i j
for (i = 0; i< 3; i++)
for (j = 0; j < 3; j++) {
if (j == i)
continue
print j
}
}' - And in these more complicated cases:
def loop_4 '{
local x
x = 3
if (x < 2)
if (x < 1)
print "Your value x is too small"
else
print "X is OK"
}'def loop_5 '{
local i
for (i = 0; i < 5; i ++) ;
print i
}'
Input/Output Facilities
The print and printf commands - write output to the screen
We already used the print command in our macros. It syntax is
print string_expression1, string_expression2, ...
The print command will write all these string_expressions to the sceen separated by one blank character. After printing it will automatically start a new line. (The possiblity to separate the string expressions with spaces and not with commas has nothing to do with the print command, but is a general SPEC feature. Strings can be concatenated by separating them with blanks. More about this feature in the section about string handling)
If you need more precise formating you can use the printf function. (the "f" stands for formated). The general syntax of this function is:
printf (format_string, argument1, argument2, ....)
The format_string describes in which format to write the parameters on the screen. There are many different possible formats and ways how to describe them. In the following we only give a short introduction to the usage of the printf function. Look in a good C reference guide to know about all the possibilities.
The general format consists of an abritary string. In this string there are special argument format descriptors embedded which start with a percentage sign (%) to distinguish them from ordinary text. This is an example how to print the contens of the variable x on the screen:
565.SPEC> printf ("The variable x is %d now\n", x)
The variable x is 0 now
In general, the argument format descriptor has the following format
|
TABLE 4. |
|
|
Field |
Description |
|
% |
percentage sign to indicate a argument format descriptor |
|
- |
optional. If the minus sign is present, the value will be left justified within the field. |
|
length |
The minimum length of the field. If the output needs more space it will extend the field. If it needs less space the remainig field will be filled with spaces unless the length has a leading 0.. If the field length has a leading 0, the field will be filled with 0's. |
|
. |
A point |
|
max_length |
A number with indicates the maximum output length. In the case of floating point numbers, this number specifies the number of digits after the decimal point. For the g format, this number specifies the number of significant digits. |
|
flag |
d,o,x,u,c,s,e,f,g Letter which indicates the type of the output. (String, Character, Floating point number,.) |
Examples of valid formats are :
%15f, %20.20s, 0x%x, %.10s, %04d, %10.3e, %-7.3f
The meaning of the letter indicating the type is shown in the following table:
|
TABLE 5. |
|||
|
Letter |
Argument |
Format |
Example |
|
d |
Integer number |
Integer number |
234 |
|
x |
|
Hexadecimal integer number |
5a3 |
|
o |
|
Octal integer number |
377 |
|
u |
|
Unsigned deciaml integer number |
45 |
|
f |
floating point number |
[-]mmm.nnnn : Fixed number of digits after the decimal point. |
34.56 |
|
e |
|
[-]m.nnnnnnE[+-]xx |
1.234e+02 |
|
g |
|
Either %f or %e whatever format is shorter. |
|
|
s |
string |
String output |
hello |
|
c |
ascii code for number |
One character. The ascii code for the character is given. |
x |
The most important usages are normally:
- %d to output a number without a decimal point
- %f to output a number with fixed number of decimal places
- %g if you are not sure about the magnitude of a floating point number
- %s to output a string
To put a literal percentage sign in the format string you have to double the percentage sign (%%). You could write for example:
printf("I work at %d %% for the moment\n", 100)
The following character combinations have a special meaning in a string constant. They are not only valid for the printf function, but in general for all the string constants.
|
TABLE 6. |
||
|
|
Description |
Decimal ASCII code |
|
\n |
The newline |
10 |
|
\r |
Carriage return |
13 |
|
\t |
Tabulator |
|
|
\b |
Backspace |
|
|
\\ |
The backslash |
|
The following examples cover the most used formats.
1.SPEC> str = "Hello world" ; i = 20; x = 234.56789; y = -1
6.SPEC> printf("This is my string %20.10s\n",str)
This is my string Hello worl
7.SPEC> printf("This is my string %-20.10s\n",str)
This is my string Hello worl
8.SPEC> printf("This is my string %-20.20s\n",str)
This is my string Hello world
9.SPEC> printf("Now i = %d and x = %f\n",i,x )
Now i = 20 and x = 234.567890
10.SPEC> printf("Now i = %04d and x = %10.2f\n",i,x )
Now i = 0020 and x = 234.57
11.SPEC> printf("Now i = %-1d and x = %10.2e\n",i,x )
Now i = 20 and x = 2.35e+02
13.SPEC> printf("Now i = 0x%x and x = %10.4g\n",i,x )
Now i = 0x14 and x = 234.6