Manuals list | Manuals list & contents | Table of contents |
Programme Flow Control in the
Optimisation Shell INVERSE
Version 0
Igor Gresovnik
Ljubljana, the 30 th of November 1998
1. Flow control *
1.1.2 Argument Passing *
1.1.2.2 Numerical Arguments *
1.1.2.3 Mathematical Expressions as Arguments *
1.1.2.4 String Arguments *
1.1.2.5 Specifications of User-defined Variables, Variable Elements and Sub-tables of Variable Elements *
1.1.2.6 Objects of Various Types *
1.1.3 Functions for Input and Output *
1.1.3.2 fwrite { arg1 arg2 ... } *
1.1.3.3 dwrite { arg1 arg2 ... } *
1.1.3.4 read { varname } *
1.2 Branches and Loops *
1.2.1.2 while { ( condition ) [ block ] } *
1.2.1.3 do { [ block ] < while > (condition) }*
1.2.1.4 interpret { filename } *
1.2.1.5 exit { < numlev > } *
1.2.1.6 block { blockcode } *
1.2.1.7 goto { labelname } *
1.2.1.8 label { labelname } *
1.2.1.9 comment { comments }, * { comments }*
1.3 Definition of New File Interpreter's Functions
*
1.3.1.2 update { code } *
1.3.1.3 numfuncargs [ ] *
Manuals list | Manuals list & contents | Table of contents |
When the optimisation shell INVERSE is run, the command file interpretation is triggered immediately after the initialisation. Everything what happens in the shell from that point on is a consequence of the file interpreter's functions (commands) called in the command file.
The branching and looping functions execute specific portions (blocks) of code if certain conditions are true. The blocks of code and conditions must be given in the argument blocks of these functions. Such functions can be nested to an arbitrary depth.
New interpreter's functions can be defined by the file interpreter commands. Their definition block of code is executed simply by calling the newly defined function.
Manuals list | Manuals list & contents | Table of contents |
funcname1 {argblock1} funcname2 {argblock2} funcname3{argblock3} ...
When the interpreter encounters a call to a function, it executes the corresponding shell's internal function associated with that interpreter's function. The interpreter does not pass the arguments from the argument block of the function call to this internal function. It only passes the information about where the argument block of the function can be found (i.e. the file and the position). The associated internal function of the shell extracts arguments from the argument block interpreting their meaning at the same time, and does what it is supposed to do.
Some functions associated with the file interpreter functions are able
to call the file interpreter to interpret specific blocks of code, usually
parts of the argument blocks of the corresponding file interpreter's functions.
The flow control is implemented by such functions.
Function arguments appear in curly brackets which follow the function name in the interpreted file. Every function call must have these curly brackets, but the brackets may be empty.
The space inside the curly brackets that follow the function name is called argument block of the function. Function arguments can be specified in this block. The file interpreter does not pass arguments to the shell's functions that are associated with the interpreter's functions. Instead, it passes the information about where the function's argument block is placed. The associated function itself must extract its arguments from the argument block. Therefore the complete freedom of interpreting arguments is left to the associated functions. The file interpreter does not impose any rules about how arguments should be interpreted.
Such freedom allows setting rules for argument formats which best suit the meaning and aim of arguments. On the other hand, this freedom allows for every function to require its own format of arguments. Therefore, some rules are imposed about the formats of arguments that are required by the file interpreter's functions. An overview of these rules is given below.
Strings must be in double quotes and may not explicitly contain the double quote characters. All double quotes within string arguments must be replaced by the appropriate sequences (\d).
Mathematical expressions are usually contained in brackets. Because all brackets in expression must be closed, the brackets which contain an expression can not be mixed up with brackets which eventually appear within the expression.
Specifications of variable's elements consist of variable name and an optional index list in square brackets. Variable name extends either until the first space or until the first square bracket. If a square bracket is encountered after the variable name, it is clear that an index list is specified and the argument extends till the closed square bracket. Otherwise there is no index list and the argument extends till the last character of the name.
Numerical arguments can be given in different ways. The most elementary
way is to specify them as numbers, e.g. 124, -3.64567, 6.02e23.
Numbers can be written in a standard format used in most programming languages.
We can specify mathematical expressions or expression evaluator's variables in place of numbers. Numerical arguments can be specified by mathematical expressions in the form
${expr}
where expr is the expression which can be evaluated by the expression evaluator. The expression does not need to be in double quotes. Spaces are allowed between the $ sign and the bracket.
The specification of numerical arguments by the expression evaluator's variables has the form
$varname
where varname is the name of the calculator's variable.
Where numerical arguments are replaced by mathematical expressions or calculator's variables, these are first evaluated in the expression evaluator and the obtained values are used as arguments. The functions associated with the file interpreter's commands take care of that.
Some examples of numerical arguments specified by mathematical expressions or calculator's variables:
${3*a+b^3} $a1 $ { getmatrix["Mat2",2,3] }
Some arguments are supposed to be mathematical expressions. Branching
and looping conditions of flow control commands are an example of that.
In this case, the expressions are not contained in curly brackets that
follow the $ sign. Numbers can be specified in place of these arguments,
but are treated as mathematical expressions and are evaluated in the expression
evaluator (although there is no need to do that).
When expressions are not the only arguments of a function, they must be somehow separated from other arguments. Usually they are contained in brackets (e.g. conditions in flow control commands). This is because expressions can contain spaces and commas which are usually used to separate arguments.
There is a simple intuitive rule about when numerical arguments and
when expression arguments are used. Expression arguments are used if the
appropriate arguments can be just numbers only in exceptional cases. This
is for example in flow control conditions or in functions like setmatrixcomponents.
String arguments are seldomly used in the file interpreter's functions.
An example of their use is in functions like write and in the execute
(system) function.
Because string arguments can contain spaces and commas, they must be in double quotes. An exception is (currently) the execute (system) command, which takes only one argument and simply takes the whole argument block as string arguments.
String arguments can contain special characters which can not be written
in interpreted ASCII files. This problem is overcome with two character
sequences which represent these characters. The sequences are replaced
by the appropriate special characters if they appear in string arguments.
The first character of all such sequences is '\'. Sequences are the folowing:
Table 1: special character sequences.
sequence | meaning | sequence | meaning |
|
single quote (') |
|
( |
|
double quote (") |
|
) |
|
backslash (\) |
|
[ |
|
null character |
|
] |
|
newline character |
|
{ |
|
carriage return |
|
} |
|
tab |
|
{ |
|
} |
Some functions take arguments that are specifications of user-defined
variables (e.g. movematrixvar), variable elements or sub-tables
of variable elements (e.g. copymatrix). We must make difference
between arguments that are specifications of variable elements of a specific
type and arguments that are objects of a specific type (later are described
in the following chapter).
Variables are specified simply by a variable name. For example, we refer to a matrix variable m1 like this:
m1
Variables can hold whole tables of objects of a specific type. When we refer to an individual element of such variable, we must specify indices of that element. Indices must be listed in square brackets which follow the variable name and be separated by spaces or commas. There can be spaces between the name and square brackets. Indices in the index list can be replaced by mathematical expressions or variables of the expression evaluator according to standard rules. The following specifications of variable element are valid and refer to the same element if the value of the expression evaluator's variable i1 is 2:
v [2,3, 2]
v[${4-2} 3 2}]
v[$i1 ${i1+1}, ${2*i1-2}]
There must be as many indices in the index list as is the number of dimensions (i.e. rank) of the appropriate variable. If we refer to an element of a variable that has rank zero, the indices are not specified. We can simply specify variable name or eventually put empty square brackets after the name:
a1
a1[ ]
We refer to sub-tables of variable elements in a similar way than to individual elements. The only difference is that the number of indices specified in the index list does not necessarily equal the number of dimensions of the appropriate variable. The specified indices refer by turns to the first few indices of the variable's element table while the rest indices remain free. All elements with the remaining indices running from 1 to the appropriate dimension specify the element sub-table. If no indices are specified, the appropriate sub-table refers to the whole element table of a given variable. When as many indices are specified as the number of variable dimensions, the appropriate sub-table contains only one element. Examples:
v [4 2]
v[3]
m [ ]
m
m[2, 4]
m [$a ${2*a}]
When arguments are data objects of different types, there are basically
two ways how to specify them in function's argument blocks. Either we specify
values of the data object directly or refer to existing data elements of
the user-defined variables of the appropriate type.
When we refer to an existent data element of a user-defined variable, this is done by the $ sign followed by the element specification (see the previous chapter!). Example:
$m2[2 4]
$ m0
In this case a copy of the specified variable element is made and this copy is used by the function to which such specification was passed in the argument block. Spaces are allowed between the $ sign and the element specification. The specification must be given as described in the previous chapter.
When we directly specify values of an object, these must be in curly brackets, and the values must be specified according to the rules that apply for a specific data type. For the rules for various data types, refer to the descriptions of the functions for setting variable elements of that specific types.
For example, we can specify the value of an matrix object this way:
{ 2 2 {{1 1:1.1}{1 2:1.2}{2 1:2.1}{2 2:2.2}} }
The value specification must be put in curly brackets. This is to avoid the ambiguities that can arise because of two reasons. First, it is not always clear how long is such specification, i.e. where the next argument starts. For example, when we specify matrices, it is allowed to specify only dimensions without components. In the above example, only "2 2" could belong to a matrix specification and the rest of the line could be something else. Secondly, numerical arguments can be replaced by an expression evaluator's variable where a variable name follows the $ sign. If the first number of the specification would be given this way, it could be understood that a matrix element of a user-defined matrix variable with rank zero was specified. For example, if we have difined an experession evaluator's variable a that has the value 2, the above line (without the outer brackets) could be replaced by the following one:
$a 2 {{1 1:1.1}{1 2:1.2}{2 1:2.1}{2 2:2.2}}
When the matrix would be read, the function that does the job could think that $a refers to the element of a zero-rank matrix variable named a, but in fact this is only a replacement for number 2 and is a part of the matrix specification. The same rules as for matrices apply for data objects of other types.
There are some exceptions at specifying data objects. Some functions
don?t accept objects given by specifications of variable elements. Furtherly,
some functions do not require that the value specification is in curly
brackets. Both apply for the functions which set elements of user defined
variables, such as setmatrix and setvector.
Manuals list | Manuals list & contents | Table of contents |
"This is a\nstring."
Expression evaluator's variables must be given by the $ character followed by the variable name. Blank characters (spaces, newlines and tabs) are alllowed between the $ character and the variable name. The current value of the expression evaluator's variable is printed. An example of an expression evaluator's variable as an argument of write is
$ v1
Mathematical expressions must be in curly bracked that follow the $ character. Blank characters are allowed between the $ character and the curly bracket. Blank characters are also allowed in the brackets since these characters are ignored in the mathematical expressions. The current value of the expression is printed.
$ { a+3+b }
Special character sequences consist of the backslash character and of the specification character that follows immediately the backslash. The appropriate special character is printed (see Table 1 for the meaning of sequences). An example of a special character sequence as an argument of write is
\t
Example:
Let us say that we have defined the expression evaluator's variables a=5 and b=2.5. Then the command
write { "The value of a is " $a "\nand the value of"
\n\t "2*a+b\n is " ${2*a+b} "." \n }
will generate output like this:
The value of a is 5
and the value of
2*a+b
is 10.
The first argument is a string and is printed literally. The second
argument is an expression evaluator's variable, therefore its current value
is printed. The third argument is again a string. It is printed literally
except that the sequence \n is replaced by the appropriate special
character, i.e. the newline. Then we have two special character sequences,
\n
and \t. The appropriate special characters, i.e. the newline and
the tabulator are printed because of them. The sixth argument is a string
again and is printed literally except that the special character sequences
are replaced by the appropriate characters. The seventh argument is a mathematical
expression and its current value is printed, i.e. 10. Then we have a string
with one character that is printed literally, and a special character sequence,
which causes the newline character to be printed.
Manuals list | Manuals list & contents | Table of contents |
Branching and looping commands execute portions of code if certain conditions are fulfilled. The expression evaluator evaluates conditions; therefore they must be given by strings which represent mathematical expressions that can be evaluated by the expression evaluator. Both conditions and portions of code must be given in the argument block of the appropriate commands.
Manuals list | Manuals list & contents | Table of contents |
Manuals list | Manuals list & contents | Table of contents |
function { for ( begin condition end body )
[
$begin
while { ( $condition )
[
$body
$end
] }
] }
This function takes four arguments: the begin block which is interpreted at the beginning and is typically an initialisation of the loop counter, the condition for the while loop, the end block which is interpreted at the end of the while block and typically includes incrementation of the counter, and the body block which represents the body of the for loop and is interpreted at the beginning of the while loop.
For example, here is a portion of code, which uses the newly defined for loop and writes numbers from 1 to 100 to the standard output:
for { ={icount:1} i<=100 ={i>i+1} { write { $i } write { "\n" } } }
When the for function is called in the above code, its arguments in the argument block replace formal arguments in the definition block of the function, which gives the following code:
={icount:1}
while { ( icount<=100 )
[
write { $icount } write { "\n" }
={icount:icount+1}
] }
This code is then executed which writes numbers from 1 to 100 to the standard output. The last argument was a block of two function calls and contained spaces, therefore it was enclosed in curly brackets.
The next example illustrates referencing the user-defined function's arguments by sequential numbers, which is mixed with standard approach, i.e. referencing by names listed in the arglist. The example shows how to define a new expression evaluator's function evaluatefunctions which evaluates an arbitrary number of expression evaluator's functions of one variable at a specific value of the independent variable and outputs the results to the standard output and programme's output file:
function {evaluatefunctions (x)
[
={icount:2}
while { (icount<=numfuncargs[])
[
update
{
write { "#{icount}[" ${$x} "] = " ${ #{icount}[$x] } \n }
fwrite { "#{icount}[" ${$x} "] = " ${ #{icount}[$x] } \n }
}
={icount:icount+1}
] }
write {\n}
fwrite {\n}
] }
The code
evalfunctions {0.0 sin cos exp}
will generate the following output:
sin[0] = 0
cos[0] = 1
exp[0] = 1
When the function is called, all references to arguments by name are immediately replaced by the strings representing function's actual arguments in the function's definition block. In the above case all strings "$x" are replaced by the string "0.0", which gives the following code of the function's definition block:
={icount:2}
while { (icount<=numfuncargs[])
[
update
{
write { "#{icount}[" ${0.0} "] = " ${ #{icount}[0.0] } \n }
fwrite { "#{icount}[" ${0.0} "] = " ${ #{icount}[0.0] } \n }
}
={icount:icount+1}
] }
write {\n}
fwrite {\n}
The replacement of the argument references by sequential numbers is accomplished each time the update command which contains the appropriate references is executed. This happens in each execution of the while loop's body. This is executed for icount=2, icount=3 and icount=4, since the number of arguments passed at the function call is 4 and this is returned by the numfuncargs function of the expression evaluator. At the first execution of the while loop's body the value of icount is 2, therefore strings #{icount} in the argument block of the update command are replaced by the string "sin" and the code which is actually executed looks like this:
write { "sin[" ${0.0} "] = " ${ sin[0.0] } \n }
fwrite { "sin[" ${0.0} "] = " ${ sin[0.0] } \n }
={icount:icount+1}
In the next pass of the while loop the value of icount is 3, therefore strings #{icount} are replaced by the string "cos" which represents the third argument passed at the function call. The appropriate code that is executed at the second pass of the while loop therefore looks like this:
write { "cos[" ${0.0} "] = " ${ cos[0.0] } \n }
fwrite { "cos[" ${0.0} "] = " ${ cos[0.0] } \n }
={icount:icount+1}
Warning:
At the definition of new functions we must be careful with the use of variables. The interpreter does not know local variables, therefore all variable names are global. Auxiliary variables in the function definitions (counters, variables for storing intermediate results, etc.) must therefore have different names as variables used for other purposes outside the function definition. Interference can usually be avoided by using long, strange or meaningless names for auxiliary variables used in the function's definition block.