Programmer Guide/STx Guru: Difference between revisions

From STX Wiki
Jump to navigationJump to search
No edit summary
No edit summary
Line 70: Line 70:


Both statements will assign the string "Rome is a city but 'Rome' is a four-letter word" to a variable called #a (although it may not always be easy later to retrieve the value of this variable). We have to leave these issues open for later discussion.
Both statements will assign the string "Rome is a city but 'Rome' is a four-letter word" to a variable called #a (although it may not always be easy later to retrieve the value of this variable). We have to leave these issues open for later discussion.
{{DISPLAYTITLE:{{SUBPAGENAME}}}}
==Variables==
The names of {{STX}} variables start with an optional one-character prefix indicating the scope of this variable (the lack of such a prefix indicating shell-global scope, see below). Besides this prefix, they may consist of letters and digits, although their first actual character must be a letter. Names are not case-sensitive, meaning that e.g. „freq", „Freq", and „FREQ", are names of the same variable.
{{STX}} discerns four kinds of scope and, hence, four kinds of variables:
{|
|-
|Scope
|Prefix
|Description
|-
|Global
|@
|Global variables are known to, and may be changed by, every shell instance, both running or yet to start. Global variables are the only kind of variables guaranteed to be persistent over interactive macro calls during one S_TOOLS<nowiki>-</nowiki>STx run.
|-
|Shell
|no prefix
|"Shell<nowiki>-</nowiki>global" variables are global to the running shell. This implies that they are known to, any may be changed by, any macro invoked by a normal macro call. The variable will not, though, be known to other running shells or to shells yet to start. So, in general, shell-global variables will not be persistent through interactive calls to a user-defined macro.N.B.: Do not mix up shell-global variables with item handles that will be dealt with in chapter [[Programmer Guide/STx Guru/Shell Items: An Overview|5]] (page 1). For the time being, suffice it to say that item handles look like shell-global variables, but that they reside in different namespaces and that they behave differently.
|-
|Local
|#
|Local variables are valid only during the run<nowiki>-</nowiki>time of one invocation of a shell macro. On each invocation of a shell macro, a separate namespace containing all its local variables is being created. This namespace is destroyed as soon as the macro finishes. Note that this implies that on invocating a macro recursively, it will find itself starting with a fresh, empty copy of all its local variables, while the calling instance will find its namespace, i.e. its local variables, untouched.
|-
|Member
|&
|When adhering to the object<nowiki>-</nowiki>oriented programming paradigm, you will define classes and instantiate them. Each instance of a class will have its own set of variables called member variables. The introductory chapters will stick to the clean ole' procedural way of programming.
|}
Variables need neither be declared nor to be explicitly initialized. If you want to introduce a variable, just start using it. Note that a variable is empty, i.e. contains the empty string, when being referred to without having been explicitly set to a value different from the void.
====Typing====
There is really not much about typing in {{STX}}. All variables store strings, like it is the case with most scripting languages. Of course these strings are at liberty only to consist of digits, a comma, and an optional sign character, making them look like numbers, smell like numbers, taste like numbers, and being treated like numbers by numerical {{STX}} functions like addition, multiplication, or even the controversial subtraction.
====Setting Variables====
Setting a variable, i.e. assigning a value to the variable, is done with the "<code>:=</code>" operator. Its general format is
<code>variable := expression</code>
=====Simple string assignment=====
In its simplest form, this "expression" is a simple string constant, just like in the following example:
<code>#adress := 'Reichsratsstrasse 17'</code>
Or even:
<code>#adress := Reichsratsstrasse 17</code>
Although generally valid, either usage is strongly discouraged, because it may lead to often surprising, seldom desired results if or when the string to be assigned starts with the name of a built-in function or a user-defined macro (note that words so far not reserved may become reserved words any time now, and that, when in a large software-building project, you never know how your colleagues call their helper macros today).
=====Typed string assignment=====
You may indicate your expressed desire for the expression to be a string constant by prefixing it with the type-selector statement "set":
<code>#address := set Reichsratsstrasse 17</code>
or, even better:
<code>#address := set 'Reichsratsstrasse 17'</code>
In this case, the assignment will even work if one day the {{STX}} macro language should be added a built-in function "Reichsratsstrasse" (which is, we dare to admit, unlikely) – or if one of your colleagues' macros happens to be likely called.
=====Typed numerical assignment=====
Although the value assigned is invariably a string, this string may be the result of a numerical computation. You may indicate your desire for it to be so by using one out of the following type-selector statements: "<code>int</code>", "<code>num</code>", and "<code>eval</code>".
The "<code>int</code>" type-selector will cause your expression being evaluated as an integer expression. More precisely (more precisely less wrongly), the expression will be evaluated numerically, and the result will be converted to an integer whose textual representation will be the string to be assigned to the destination variable. The calculation itself will be done with the point floating, though (see the below examples for what that means).
The "<code>num</code>" type-selector will cause your expression being evaluated as a numerical expression, provided it is such. The textual representation of the numerical result will be the string to assign to the destination variable.
The "<code>eval</code>" type-selector is the most powerful of them all. Firstly, it does everything the "<code>num</code>" type-selector does. So, when evaluating a plain numerical expression, it is your free choice whether to use "<code>num</code>" or "<code>eval</code>" (we might one day choose spontaneously to fade out the "<code>num</code>" type-selector, but do not allow this to bias your choice). But, secondly: The "<code>eval</code>" type-selector is capable of much, much more: It does vector and matrix operations of the most sophisticated kind, calculates averages, converts units, dances the Fourier transform, and so on. The actual number of functions available to "<code>eval</code>" expressions is more than 70 and counting (see the interactive {{STX}} help topics "EVAL" and "EVAL subcommands").
For an example, compare the following statements:
<code>#a := num 3*3.4 // result is 10.19999999999</code>
<code>#a := int 3*3.4 // result is 10 (!)</code>
<code>#a := num 3*int(3.4) // result is 9</code>
<code>#a := int 3*int(3.4) // result is 9</code>
<code>#a := num int(3*3.4) // result is 10 (!)</code>
<code>#a := num int(3*int(3.4)) // result is 9</code>
Here the first statement will cause <code>#a</code> to be assigned the result of the floating point multiplication of 3 and 3.4, i.e. about 10.2. In the second statement, the same multiplication will be calculated, and only the result of this calculation, i.e. 10.2 (roughly...), will get truncated to an integer – this integer in turn being 10. Only the third and the fourth statement (both!) will cause the second factor, 3.4, to be broken down to an integer before multiplication. This usage, though, is strictly not a feature of the type selector-based assignment, but of the numerical function (we will come to these later) "int". (Note that since the product of two integers is an integer itself, in the third and fourth statement it does not make any difference whether we use the "num" or the "int" type selector.)
Do not mix up the type selector of the assignment with any built-in type-conversion functions: Whereas the type-selector always immediately follows the assignment operator, "<code>:=</code>", type-conversion functions never do. Furthermore, the arguments of type-conversion functions are always enclosed in brackets, whereas the type selector never uses brackets.
So in the third to sixth example, the type selectors are "<code>num</code>", "<code>int</code>", "<code>num</code>", and again "<code>num</code>", whereas the strings "<code>int(3.4)</code>", "<code>int(3*3.4)</code>", and "<code>int(3*int(3.4))</code>" are calls to the built-in type conversion function, "<code>int</code>". (If this sounds confusing for the moment, you should not worry: In practice, things are much easier, and it is not normally necessary to think these things over).
=====Function calls=====
Syntactically, every built-in {{STX}} function, every macro, and every method of a user-defined class may be the source of an assignment. If this happens to be the case, the respective function or macro is executed, and its result is assigned to the destination variable. See it for yourself:
<code>#i := word 2 one two three four // #i will be "three"</code>
The built-in "<code>word</code>" function takes an integer index and a list of strings as its arguments. From this list, it selects and returns the string with the respective index. Hence, the expression "<code>word 2 one two three four</code>" will select, and return, the third word, „three". When part of an assignment as in the above example, this very string "three" will be assigned to the respective destination variable, in this case: to the local variable <code>#i</code>.
The same holds true if the source of the assignment is the name of a class method. In this case, the respective method is being called, and its result (which may as well be the empty string) gets assigned to the destination variable.
The issue of function calls will be dealt with in full detail in chapter 3.2.4, Calls to Macros and Built-In Functions (page 1).
====Accessing Variables====
If and when you want to access the value of a variable (bluntly put: to read out its content), you need to put a dollar sign in front of the variable name. What happens internally is that, before actually executing a line from the macro file, {{STX}} replaces all occurrences of variable names that are prefixed by a dollar sign by the content of the respective variables. See for yourself:
<code>#i := int 7</code>
<code>writelog 'The current value of variable #i is $#i'</code>
<code>#i := int $#i + 1 // #i will be set to 8</code>
<code>#heading := set 'This is page $#curpage out of $#totpage'</code>
The first line in this example will assign the value 7 to a local variable called <code>#i</code>. This is nothing new; note that we are using the type-selector "<code>int</code>" to make sure the assigned value is interpreted as an integer, though in this case this is strictly redundant because 7 cannot help being an integer anyway.
The second line will print out the text "The current value of variable #i is 7". This should not be surprising, for the name of a variable is only replaced by its content if preceded by a dollar sign. Hence, the first occurrence of "#i" – although we know that there is a variable called #i – does not get replaced by that variable's value. The second, though, does, because it is preceded by the dollar sign. {{STX}} is one reliable piece of software, strictly and indiscriminately following the instructions laid out by its master.
What the third line does is increase the value of the local variable #i by 1. What's more interesting is how it does so. First, {{STX}} replaces all dollar-prefixed variable names by the contents of the respective variables. Hence, {{STX}} will replace the string "$#i" by the current value of <code>#i</code> which, in our example, happens to be 7. This replacement will change the current statement from "#<code>i := int $#i + 1</code>" to "<code>#i := int 7 + 1</code>". Now this is one fine integer expression that in turn gets evaluated to 8, thereby causing 8 to be the string that is finally assigned to the destination variable.
The fourth and last line demonstrates that substitution also works within string constants, and that it does so even if they are put under quotation marks (in our case, apostrophes). Some macro languages, e.g. the well-known UNIX shells, let certain kinds of quotation marks prevent substitution. Users familiar with such shells should bear in mind that {{STX}} is a kind of its own. (Note that if you really want to suppress the special meaning of a character like the dollar sign, you may precede it with the {{STX}} escape character "<code>`</code>", the so-called back-tick.)
The aspiring {{STX}} guru may find it instructive to consider the following example (anyone else will find no harm in completely skipping this example):
<code>#var := set 'one'</code>
<code>writelog '#var now containing "$#var"'</code>
<code>#var := set 'two'</code>
<code>writelog '#var now containing "$#var"'</code>
<code>$#var := set 'three' // N.B.: substitution will make this</code>
<code>// "two := set 'three'"</code>
<code>writelog '#var still containing "$#var"'</code>
<code>writelog '...but there suddenly is a variable called two'</code>
<code>writelog '...and its value is "$two"'</code>
The first line is well familiar. It assigns the string "one" to a local variable called "<code>#var</code>". Consequently, the second line will print exactly the following string:
<code>#var now containing "one"</code>
The third line changes the value of <code>#var</code> to "two", hence the third line will print out the following string:
<code>#var now containing "two"</code>
No surprises yet. But what will the fourth line do? Well, not to be surprised about the answer to this semi-rhetorical question, we must analyze the statement carefully. It reads "<code>$#var := set 'three'</code>" – did you notice that the assignment target is preceded by a dollar sign? Alas, this instructs {{STX}} to replace the variable name by its content; but the content of variable "<code>#var</code>" is "two." Hence the statement gets, by substitution, altered to "<code>two := set 'three'</code>". This is a perfectly valid assignment statement, only that the target of the assignment is a shell-global variable called "<code>two</code>". So we assign the string "three" to a variable called "<code>two</code>". The next statements only illustrate this fact by printing out the respective values.
====Read Functions====
The "<code>read</code>" family of functions supply a means for parsing the contents of a string or a variable, that is for splitting them into several pieces, and for storing some or all of these pieces into one or more other variables (or even in the same variable). That being said, it should be noticed that everything is much easier than this description implicates. See for yourself:
<code>readstr 'one two three four five' #a #b #c /Delete</code>
<code>// #a is now "one", #b is now "two", #c is "three four five"</code>
<code>writelog 'now a="$#a", b="$#b", and c="$#c".'</code>
The <code>readstr</code> command parses its first argument into (at most) as many blank-separated strings as there are variables. If the number of variables is higher than the number of available words, the remaining variables will either be cleared (if supplying the <code>/Delete</code> option), or they will be left untouched (if omitting the <code>/Delete</code> option). If, on the other hand, the number of variables is lower than the number of available words, the last variable gets all the remaining words. Note that if there is more than one whitespace character between two words, this will not do any harm.
So what the above example does is parse the string "one two three four five" into three substrings (there are three variables supplied, <code>#a</code>, <code>#b</code>, and <code>#c</code>). The first substring will be the first word, "one". The second substring will be the second word, "two". Since there are more words than variables, the third substring will catch all the rest, that is, the string "three four five". It's really simple, isn't it?
There is one additional feature you may, or may not, find convenient. You may as well parse strings that are separated by exactly one character of your choice. If, for some reason, you prefer semicolons over blanks, you might have done the above example as follows:
<code>readstr 'one;two;three;four;five' #a ';' #b ';' #c /Delete</code>
<code>// #a is now "one", #b is now "two", #c is "three;four;five"</code>
<code>writelog 'now a="$#a", b="$#b", and c="$#c".'</code>
The syntactical difference between those two examples is that the latter explicitly names the separator character between each pair of variables. The difference in semantics is that now there must be exactly one separator character between two words. So this variant of the <code>readstr</code> command empowers you to read empty words, two. Consider the following statement (we may abbreviate the <code>/Delete</code> option to <code>/D</code>, if we do not care for the reduced legibility):
<code>readstr 'one;;two;three;four;five' #a ';' #b ';' #c /D</code>
<code>// #a is "one", #b is empty, #c is " two;three;four;five"</code>
<code>writelog 'now a="$#a", b="$#b", and c="$#c".'</code>
Looking awfully identical, doesn't it? Well, instead of one semicolon, there are now two semicolons between the first two words, "one" and "two". Believe it or not, this is making all the difference in the world: {{STX}} will consider these successive semicolons three separate words, the first being "one", the second one being empty, and the third one being, in general, "two" (in our case where there are only three variables supplied, the third word will get the rest of the string). So, with this example, variable #a will get the string "one", variable #b will be cleared, and variable #c will get the rest, that is the string, "two;three;four;five". Cool, isn't it?
Note that if you omit the <code>/Delete</code> option, target variables corresponding to empty words will not be cleared, that is, they will keep whatever value that had before calling <code>readstr</code>. That being said, you can easily foretell the results of the following statements:
<code>b := set 'old value before calling readstr'</code>
<code>readstr 'one;;two;three;four;five' #a ';' #b ';' #c</code>
<code>writelog 'now a="$#a", b="$#b", and c="$#c".'</code>
As you rightly foretold, the <code>readstr</code> command in this example will not change the value of the second variable, <code>#b</code>, since the second word in this string is empty.
Although technically a consequence of the above, it may not immediately be clear that the space character, too, may explicitly named as separating arguments – and that this does cause a difference to the default <code>readstr</code> behaviour. Look for yourself (and notice that there are two space characters between the "a" and the "b" in the first string constant):
<code>readstr 'a b c' #a #b #c /Delete</code>
<code>// #a is now "a", #b is "b", #c is "c"</code>
<code>writelog 'a="$#a", b="$#b", c="$#c"'</code>
<code>readstr 'a b c' #a ' ' #b ' ' #c /Delete</code>
<code>// #a is now "a", #b is empty, #c is "b c"</code>
<code>writelog 'a="$#a", b="$#b", c="$#c"'</code>
As you already know, there is one important difference between both <code>readstr</code> variants: When explicitly naming the separation character, {{STX}} considers consecutive occurrences of the separation character to separate empty strings. When not naming a separation, consecutive occurrences of whitespace are considered one single separator character, thereby causing no empty word to be read. So, in the above example, the first <code>readstr</code> command will read "a" into #a, "b" into #b, and "c" into #c, whereas the second <code>readstr</code> command will read "a" into #a, the empty word into #b, and the remaining string, "b c", into the last variable, #c.
Of course the string argument supplied to <code>readstr</code> may even be the result of variable substitution. Less prosaically put, you might as well supply code like the following:
<code>#three := set 'THREE'</code>
<code>readstr 'one two $#three four' #a #b #c #d</code>
<code>writelog 'now a="$#a", b="$#b", c="$#c", d="$#d"'</code>
Before executing the command, {{STX}} will, as usual, look for any variable name prefixed with a dollar sign. If there happens to be any, they will be replaced by the contents of the respective variables. So in the above example, "$#three" will get replaced by the contents of variable <code>#three</code>, thereby causing the <code>readstr</code> command actually to be processed to be the following:
<code>readstr 'one two THREE four' #a #b #c #d</code>
Only the strong survive the following example (anyone else will find no harm in skipping it).
<code>readstr 'x y z' #a #b #c</code>
<code>writelog 'now a="$#a", b="$#b", c="$#c"'</code>
<code>readstr 'one two three' $#a $#b $#c</code>
<code>writelog 'gee, now a="$#a", b="$#b", c="$#c"'</code>
<code>writelog 'but mysteriously, x="$x", y="$y", z="$z"'</code>
When listening very carefully, you might hear the above example speak for itself. If not, you will find the key in the third line that contains the second <code>readstr</code> command. Did you notice the variables being prefixed by a dollar sign each? Now the dollar sign indicates {{STX}} that variable substitution is desired (and required) before the command is to be processed. So all {{STX}} does is replace the two strings "<code>$#a</code>", "<code>$#b</code>", and "<code>$#c</code>" by the contents of the respective variables. Since, at this stage, their contents are "x", "y", and "z", the command actually to get executed will look as follows:
<code>readstr 'one two three' x y z</code>
Now this clearly is a request to parse the string "one two three" into the three shell-global variables "<code>x</code>", "<code>y</code>", and "<code>z</code>".
When reading directly from one variable, you might prefer a variant of <code>readstr</code>, the <code>readvar</code> command. <code>Readvar</code> works similar to <code>readstr</code>, as the following example shows:
<code>#var := set 'one two three'</code>
<code>readvar #var #a #b #c /Delete</code>
The first argument to <code>readvar</code> is one variable to read from. The remaining arguments are the variables where to store the words read. Otherwise that <code>readvar</code> reads from a variable as opposed to reading from a literal string, there is no difference between <code>readvar</code> and <code>readstr</code>.
You will undoubtedly ask why there is such a thing as a <code>readvar</code> command. After all, all that
<code>readvar #var #a #b #c</code>
does may as well be done using the following command:
<code>readstr '$#var' #a #b #c</code>
You are, of course, right in principle. The difference between the two commands is that the latter depends upon variable substitution which introduces a second step when evaluating and executing the command. Hence, the former is simply faster.
Note that there is a <code>readtable</code> function, too. We will come to that much later when dealing with the versatile (both are) {{STX}} table feature.
====Special Variables====
There are a number of reserved variables that serve special purposes. At this stage, it suffices to give a short overview of the most important special variables.
{|
|-
|Variable
|Description
|-
|<code>rc</code>
|After executing an S_TOOLS<nowiki>-</nowiki>STx statement or built-in function (not a user-defined macro!), this variable contains its numerical return code, 0 indicating success, and values different from 0 indicating different kinds of failure. "<code>rc</code>" gets set after each invocation of an {{STX}} statement or built-in function, meaning that an error code will get reset when executing the next statement.
|-
|<code>emsg</code>
|This variable contains a textual description of the value of the "<code>rc</code>" variable. It, too, gets reset with each new {{STX}} statement.
|-
|<code>#argc, #argv</code>
|On macro invocation, "<code>argc</code>" contains the number of arguments supplied to the respective macro, while "<code>argv</code>" contains the actual arguments (all of them). See below.
|-
|<code>result</code>
|After returning from a user<nowiki>-</nowiki>defined macro call, the variable "<code>result</code>" contains the value returned by the respective macro, i.e. the value the macro supplied as an argument to the "<code>exit</code>" call. If the macro was left without returning an argument, "<code>result</code>" is empty (this is not considered an error – honestly, there is not much that is ever considered an error as far as {{STX}} is concerned).
|-
|<code>#read</code>
|After using one of the S_TOOLS<nowiki>-</nowiki>STx commands <code>readstr</code>, <code>readvar</code> or <code>readtab</code>, this variable contains the number of arguments actually read.
|-
|<code>#new</code>
|When allocating an S_TOOLS<nowiki>-</nowiki>STx shell item, its item name will be stored in a variable called "<code>#new</code>". On the issue of shell items, please be patient until chapter [[Programmer Guide/STx Guru/Shell Items: An Overview|5]] on page 1.
|}
In general, even the reserved variable may be target of an assignment (this is sometimes used with the "<code>#argv</code>" variable for implementing default macro arguments). You should not be surprised, though, that assigning a value to either "<code>rc</code>" or "<code>emsg</code>" will not have the desired outcome: Since the assignment statement is built-in {{STX}} statement itself, executing it will reset "<code>rc</code>" to 0 and "<code>emsg</code>" to the empty string, thereby indicating that the assignment statement itself was successful (which it was).





Revision as of 22:30, 13 April 2011

Whom this is for

This manual is a general introduction to the programming language that is part of the STx environment. Reading this manual will allow you to implement both procedural functions and object-oriented classes for a wide range of tasks, with a natural focus on numerical applications, sound processing, and visualization. As a reader of this manual, you should be slightly familiar with programming in general, and it would be great if you were an avid user of STx (on the other hand, being faintly familiar with starting up STx, loading sound files, and maybe even starting the spectrogram function should suffice).

This manual is intended for reading from start to end (not necessarily without interruptions). It is not a reference manual, meaning that it will abstract from many details (you might even bluntly say: omit them) in order not to depress the reader with a seemingly abundant amount of material. Instead, a careful selection has been taken on what is, and what is not, necessary for achieving common goals. Of course this selectivity (or, if you prefer, these omissions) try hard not to give any false impressions of what can, and what can't, be done with STx (and how). We are well aware that writing such an introductory programmer's (or, as we hope, programmers') manual is a slippery slope, and we hope for the reader's (or, as we hope, the readers') pardon if the depth covering each topic is too shallow, or to deep, or both (we are not quite sure whether the latter is logically possible, but one never knows). We appreciate any comments or criticism, both on this manual and on STx.

If you want to dig deeper into any specific topic, or if you are looking for information on a specific issue, we recommend having a look at the online help of STx (just start STx and select the "Help" menu, or press the "F1" key). The online help is a vast and ever-growing collection of deep, sometimes even exhausting wisdom. It is, in fact, the official reference manual. When new to STx programmers, we recommend first reading this manual (you might skip paragraphs you find particularly uninteresting), probably trying out all the examples by yourself, preferably even modifying and improving them. Afterwards, you will have a basis firm enough for using the STx online help as a reference manual. And you will surely have a sound basis for solving all your future programming tasks with STx, disposing of the need for any other programming languages or environments.


Constants

STx does not strictly discern between string constants and numerical constants. Normally each constant is considered a string, there only being exceptions dependent on the context where the constant occurs. If, for example, a constant occurs as part of a numerical expression, STx tries to get its numerical value.

Generally, you may, but you need not, put single quotes around constants. With a few exceptions depending on context, this will not change the way STx handles the constant. So each of the following strings is a valid STx constant:

string1

'string1'

-12.34

'-12.34'

Regardless of the presence or absence of single quotes, both the first and the second argument will be considered string constants. Also regardless of the presence or absence of single quotes, both the third and the fourth constant will be considered numerical constants when occurring in a numerical context, or string constants when occurring in a string context.

If a string constant contains whitespace characters, it depends on the context whether it is considered one string constant or more than one string constant. If you want to make sure that a string constant is considered one constant, you should always put single quotes around the whole affair:

'Hello World'

Hello World

While the first string is always considered one string constant, the second one may, depending on where it is occurring, be considered one string constant denoting the string "Hello World", or two string constants, denoting the strings "Hello" and "World", respectively.

These issues will be dealt with in more detail below. For the moment, the curious reader may have a look at the following assignment statements:

#a := num '5' * '3' // value of #a will be 15

#b := set 5 * 3 // value of #a will be "5 * 3"

The first statement is a numerical assignment (denoted by the keyword "num"). Both constants, 5 and 3, will be considered numerical constants, even if surrounded by quote characters. On the other hand, the second statement is a string assignment (denoted by the keyword "set"). So the argument will, logically, be interpreted as one string constant, "5 * 3", even though it contains whitespace and lacks any quote character. What happens physically is that all separate words will be concatenated to the one string constant expected, inserting exactly one blank between each pair of words. The following statements show the consequences of this procedure:

#b := set 5 * 3 // value of #b will be "5 * 3"

#b := set 5 * 3 // value of #b will be "5 * 3", too

#b := set '5 * 3' // value of #b will be "5 * 3"

In the second statement, though the words "5", "*", and "3" are separated by more than one whitespace character, the one string that will be built up from them is "5 * 3" – when concatenating them, they get separated by exactly one space character.

You may influence the way concatenation works by quoting some, or all, of the words to concatenate. Quoting a word will prevent STx from automatically inserting a blank before and after that word. So, compare the above statements with the statements below:

#b := set 5 '*' 3 // #b is "5*3" (no space)

#b := set '5' * 3 // #b is "5* 3" (one space)

#b := set 5 ' * ' 3 // #b is "5 * 3" (three spaces)

With the first statement, the word in the middle, "*", is quoted. This indicates STx on concatenation not to insert a space character either before or after this word, resulting in #b being set to "5*3" (no intervening whitespace).

With the second statement, the first word is quoted and will, hence, be concatenated to its right successor (there is no left predecessor) without inserting space. The second and the third word are not quoted and will be concatenated with an additional space in between them. This results in #b being assigned "5* 3" (no whitespace between "5" and "*", one blank between "*" and "3").

With the third statement, concatenation will not add any additional blanks either before or after the word in the middle. The whitespace that is part of the word, i.e. part of the quotation (three blanks before and after the asterisk, each), will be unaltered, though. So what results it #b being assigned the string "5 * 3" (exactly three blanks both before and after the asterisk).

Within a constant, you may alter the meaning of special characters by using the STx escape character "`", the backwards single quote, sometimes called back-tick. At the current stage, we can only use this feature for defining a string constant that contains single quote characters themselves:

#a := set 'Rome is a city but `'Rome`' is a four-letter word'

#a := set Rome is a city but `'Rome`' is a four-letter word

Both statements will assign the string "Rome is a city but 'Rome' is a four-letter word" to a variable called #a (although it may not always be easy later to retrieve the value of this variable). We have to leave these issues open for later discussion.


Variables

The names of STx variables start with an optional one-character prefix indicating the scope of this variable (the lack of such a prefix indicating shell-global scope, see below). Besides this prefix, they may consist of letters and digits, although their first actual character must be a letter. Names are not case-sensitive, meaning that e.g. „freq", „Freq", and „FREQ", are names of the same variable.

STx discerns four kinds of scope and, hence, four kinds of variables:

Scope Prefix Description
Global @ Global variables are known to, and may be changed by, every shell instance, both running or yet to start. Global variables are the only kind of variables guaranteed to be persistent over interactive macro calls during one S_TOOLS-STx run.
Shell no prefix "Shell-global" variables are global to the running shell. This implies that they are known to, any may be changed by, any macro invoked by a normal macro call. The variable will not, though, be known to other running shells or to shells yet to start. So, in general, shell-global variables will not be persistent through interactive calls to a user-defined macro.N.B.: Do not mix up shell-global variables with item handles that will be dealt with in chapter 5 (page 1). For the time being, suffice it to say that item handles look like shell-global variables, but that they reside in different namespaces and that they behave differently.
Local # Local variables are valid only during the run-time of one invocation of a shell macro. On each invocation of a shell macro, a separate namespace containing all its local variables is being created. This namespace is destroyed as soon as the macro finishes. Note that this implies that on invocating a macro recursively, it will find itself starting with a fresh, empty copy of all its local variables, while the calling instance will find its namespace, i.e. its local variables, untouched.
Member & When adhering to the object-oriented programming paradigm, you will define classes and instantiate them. Each instance of a class will have its own set of variables called member variables. The introductory chapters will stick to the clean ole' procedural way of programming.

Variables need neither be declared nor to be explicitly initialized. If you want to introduce a variable, just start using it. Note that a variable is empty, i.e. contains the empty string, when being referred to without having been explicitly set to a value different from the void.

Typing

There is really not much about typing in STx. All variables store strings, like it is the case with most scripting languages. Of course these strings are at liberty only to consist of digits, a comma, and an optional sign character, making them look like numbers, smell like numbers, taste like numbers, and being treated like numbers by numerical STx functions like addition, multiplication, or even the controversial subtraction.

Setting Variables

Setting a variable, i.e. assigning a value to the variable, is done with the ":=" operator. Its general format is

variable := expression

Simple string assignment

In its simplest form, this "expression" is a simple string constant, just like in the following example:

#adress := 'Reichsratsstrasse 17'

Or even:

#adress := Reichsratsstrasse 17

Although generally valid, either usage is strongly discouraged, because it may lead to often surprising, seldom desired results if or when the string to be assigned starts with the name of a built-in function or a user-defined macro (note that words so far not reserved may become reserved words any time now, and that, when in a large software-building project, you never know how your colleagues call their helper macros today).

Typed string assignment

You may indicate your expressed desire for the expression to be a string constant by prefixing it with the type-selector statement "set":

#address := set Reichsratsstrasse 17

or, even better:

#address := set 'Reichsratsstrasse 17'

In this case, the assignment will even work if one day the STx macro language should be added a built-in function "Reichsratsstrasse" (which is, we dare to admit, unlikely) – or if one of your colleagues' macros happens to be likely called.

Typed numerical assignment

Although the value assigned is invariably a string, this string may be the result of a numerical computation. You may indicate your desire for it to be so by using one out of the following type-selector statements: "int", "num", and "eval".

The "int" type-selector will cause your expression being evaluated as an integer expression. More precisely (more precisely less wrongly), the expression will be evaluated numerically, and the result will be converted to an integer whose textual representation will be the string to be assigned to the destination variable. The calculation itself will be done with the point floating, though (see the below examples for what that means).

The "num" type-selector will cause your expression being evaluated as a numerical expression, provided it is such. The textual representation of the numerical result will be the string to assign to the destination variable.

The "eval" type-selector is the most powerful of them all. Firstly, it does everything the "num" type-selector does. So, when evaluating a plain numerical expression, it is your free choice whether to use "num" or "eval" (we might one day choose spontaneously to fade out the "num" type-selector, but do not allow this to bias your choice). But, secondly: The "eval" type-selector is capable of much, much more: It does vector and matrix operations of the most sophisticated kind, calculates averages, converts units, dances the Fourier transform, and so on. The actual number of functions available to "eval" expressions is more than 70 and counting (see the interactive STx help topics "EVAL" and "EVAL subcommands").

For an example, compare the following statements:

#a := num 3*3.4 // result is 10.19999999999

#a := int 3*3.4 // result is 10 (!)

#a := num 3*int(3.4) // result is 9

#a := int 3*int(3.4) // result is 9

#a := num int(3*3.4) // result is 10 (!)

#a := num int(3*int(3.4)) // result is 9

Here the first statement will cause #a to be assigned the result of the floating point multiplication of 3 and 3.4, i.e. about 10.2. In the second statement, the same multiplication will be calculated, and only the result of this calculation, i.e. 10.2 (roughly...), will get truncated to an integer – this integer in turn being 10. Only the third and the fourth statement (both!) will cause the second factor, 3.4, to be broken down to an integer before multiplication. This usage, though, is strictly not a feature of the type selector-based assignment, but of the numerical function (we will come to these later) "int". (Note that since the product of two integers is an integer itself, in the third and fourth statement it does not make any difference whether we use the "num" or the "int" type selector.)

Do not mix up the type selector of the assignment with any built-in type-conversion functions: Whereas the type-selector always immediately follows the assignment operator, ":=", type-conversion functions never do. Furthermore, the arguments of type-conversion functions are always enclosed in brackets, whereas the type selector never uses brackets.

So in the third to sixth example, the type selectors are "num", "int", "num", and again "num", whereas the strings "int(3.4)", "int(3*3.4)", and "int(3*int(3.4))" are calls to the built-in type conversion function, "int". (If this sounds confusing for the moment, you should not worry: In practice, things are much easier, and it is not normally necessary to think these things over).

Function calls

Syntactically, every built-in STx function, every macro, and every method of a user-defined class may be the source of an assignment. If this happens to be the case, the respective function or macro is executed, and its result is assigned to the destination variable. See it for yourself:

#i := word 2 one two three four // #i will be "three"

The built-in "word" function takes an integer index and a list of strings as its arguments. From this list, it selects and returns the string with the respective index. Hence, the expression "word 2 one two three four" will select, and return, the third word, „three". When part of an assignment as in the above example, this very string "three" will be assigned to the respective destination variable, in this case: to the local variable #i.

The same holds true if the source of the assignment is the name of a class method. In this case, the respective method is being called, and its result (which may as well be the empty string) gets assigned to the destination variable.

The issue of function calls will be dealt with in full detail in chapter 3.2.4, Calls to Macros and Built-In Functions (page 1).

Accessing Variables

If and when you want to access the value of a variable (bluntly put: to read out its content), you need to put a dollar sign in front of the variable name. What happens internally is that, before actually executing a line from the macro file, STx replaces all occurrences of variable names that are prefixed by a dollar sign by the content of the respective variables. See for yourself:

#i := int 7

writelog 'The current value of variable #i is $#i'

#i := int $#i + 1 // #i will be set to 8

#heading := set 'This is page $#curpage out of $#totpage'

The first line in this example will assign the value 7 to a local variable called #i. This is nothing new; note that we are using the type-selector "int" to make sure the assigned value is interpreted as an integer, though in this case this is strictly redundant because 7 cannot help being an integer anyway.

The second line will print out the text "The current value of variable #i is 7". This should not be surprising, for the name of a variable is only replaced by its content if preceded by a dollar sign. Hence, the first occurrence of "#i" – although we know that there is a variable called #i – does not get replaced by that variable's value. The second, though, does, because it is preceded by the dollar sign. STx is one reliable piece of software, strictly and indiscriminately following the instructions laid out by its master.

What the third line does is increase the value of the local variable #i by 1. What's more interesting is how it does so. First, STx replaces all dollar-prefixed variable names by the contents of the respective variables. Hence, STx will replace the string "$#i" by the current value of #i which, in our example, happens to be 7. This replacement will change the current statement from "#i := int $#i + 1" to "#i := int 7 + 1". Now this is one fine integer expression that in turn gets evaluated to 8, thereby causing 8 to be the string that is finally assigned to the destination variable.

The fourth and last line demonstrates that substitution also works within string constants, and that it does so even if they are put under quotation marks (in our case, apostrophes). Some macro languages, e.g. the well-known UNIX shells, let certain kinds of quotation marks prevent substitution. Users familiar with such shells should bear in mind that STx is a kind of its own. (Note that if you really want to suppress the special meaning of a character like the dollar sign, you may precede it with the STx escape character "`", the so-called back-tick.)

The aspiring STx guru may find it instructive to consider the following example (anyone else will find no harm in completely skipping this example):

#var := set 'one'

writelog '#var now containing "$#var"'

#var := set 'two'

writelog '#var now containing "$#var"'

$#var := set 'three' // N.B.: substitution will make this

// "two := set 'three'"

writelog '#var still containing "$#var"'

writelog '...but there suddenly is a variable called two'

writelog '...and its value is "$two"'

The first line is well familiar. It assigns the string "one" to a local variable called "#var". Consequently, the second line will print exactly the following string:

#var now containing "one"

The third line changes the value of #var to "two", hence the third line will print out the following string:

#var now containing "two"

No surprises yet. But what will the fourth line do? Well, not to be surprised about the answer to this semi-rhetorical question, we must analyze the statement carefully. It reads "$#var := set 'three'" – did you notice that the assignment target is preceded by a dollar sign? Alas, this instructs STx to replace the variable name by its content; but the content of variable "#var" is "two." Hence the statement gets, by substitution, altered to "two := set 'three'". This is a perfectly valid assignment statement, only that the target of the assignment is a shell-global variable called "two". So we assign the string "three" to a variable called "two". The next statements only illustrate this fact by printing out the respective values.

Read Functions

The "read" family of functions supply a means for parsing the contents of a string or a variable, that is for splitting them into several pieces, and for storing some or all of these pieces into one or more other variables (or even in the same variable). That being said, it should be noticed that everything is much easier than this description implicates. See for yourself:

readstr 'one two three four five' #a #b #c /Delete

// #a is now "one", #b is now "two", #c is "three four five"

writelog 'now a="$#a", b="$#b", and c="$#c".'

The readstr command parses its first argument into (at most) as many blank-separated strings as there are variables. If the number of variables is higher than the number of available words, the remaining variables will either be cleared (if supplying the /Delete option), or they will be left untouched (if omitting the /Delete option). If, on the other hand, the number of variables is lower than the number of available words, the last variable gets all the remaining words. Note that if there is more than one whitespace character between two words, this will not do any harm.

So what the above example does is parse the string "one two three four five" into three substrings (there are three variables supplied, #a, #b, and #c). The first substring will be the first word, "one". The second substring will be the second word, "two". Since there are more words than variables, the third substring will catch all the rest, that is, the string "three four five". It's really simple, isn't it?

There is one additional feature you may, or may not, find convenient. You may as well parse strings that are separated by exactly one character of your choice. If, for some reason, you prefer semicolons over blanks, you might have done the above example as follows:

readstr 'one;two;three;four;five' #a ';' #b ';' #c /Delete

// #a is now "one", #b is now "two", #c is "three;four;five"

writelog 'now a="$#a", b="$#b", and c="$#c".'

The syntactical difference between those two examples is that the latter explicitly names the separator character between each pair of variables. The difference in semantics is that now there must be exactly one separator character between two words. So this variant of the readstr command empowers you to read empty words, two. Consider the following statement (we may abbreviate the /Delete option to /D, if we do not care for the reduced legibility):

readstr 'one;;two;three;four;five' #a ';' #b ';' #c /D

// #a is "one", #b is empty, #c is " two;three;four;five"

writelog 'now a="$#a", b="$#b", and c="$#c".'

Looking awfully identical, doesn't it? Well, instead of one semicolon, there are now two semicolons between the first two words, "one" and "two". Believe it or not, this is making all the difference in the world: STx will consider these successive semicolons three separate words, the first being "one", the second one being empty, and the third one being, in general, "two" (in our case where there are only three variables supplied, the third word will get the rest of the string). So, with this example, variable #a will get the string "one", variable #b will be cleared, and variable #c will get the rest, that is the string, "two;three;four;five". Cool, isn't it?

Note that if you omit the /Delete option, target variables corresponding to empty words will not be cleared, that is, they will keep whatever value that had before calling readstr. That being said, you can easily foretell the results of the following statements:

b := set 'old value before calling readstr'

readstr 'one;;two;three;four;five' #a ';' #b ';' #c

writelog 'now a="$#a", b="$#b", and c="$#c".'

As you rightly foretold, the readstr command in this example will not change the value of the second variable, #b, since the second word in this string is empty.

Although technically a consequence of the above, it may not immediately be clear that the space character, too, may explicitly named as separating arguments – and that this does cause a difference to the default readstr behaviour. Look for yourself (and notice that there are two space characters between the "a" and the "b" in the first string constant):

readstr 'a b c' #a #b #c /Delete

// #a is now "a", #b is "b", #c is "c"

writelog 'a="$#a", b="$#b", c="$#c"'

readstr 'a b c' #a ' ' #b ' ' #c /Delete

// #a is now "a", #b is empty, #c is "b c"

writelog 'a="$#a", b="$#b", c="$#c"'

As you already know, there is one important difference between both readstr variants: When explicitly naming the separation character, STx considers consecutive occurrences of the separation character to separate empty strings. When not naming a separation, consecutive occurrences of whitespace are considered one single separator character, thereby causing no empty word to be read. So, in the above example, the first readstr command will read "a" into #a, "b" into #b, and "c" into #c, whereas the second readstr command will read "a" into #a, the empty word into #b, and the remaining string, "b c", into the last variable, #c.

Of course the string argument supplied to readstr may even be the result of variable substitution. Less prosaically put, you might as well supply code like the following:

#three := set 'THREE'

readstr 'one two $#three four' #a #b #c #d

writelog 'now a="$#a", b="$#b", c="$#c", d="$#d"'

Before executing the command, STx will, as usual, look for any variable name prefixed with a dollar sign. If there happens to be any, they will be replaced by the contents of the respective variables. So in the above example, "$#three" will get replaced by the contents of variable #three, thereby causing the readstr command actually to be processed to be the following:

readstr 'one two THREE four' #a #b #c #d

Only the strong survive the following example (anyone else will find no harm in skipping it).

readstr 'x y z' #a #b #c

writelog 'now a="$#a", b="$#b", c="$#c"'

readstr 'one two three' $#a $#b $#c

writelog 'gee, now a="$#a", b="$#b", c="$#c"'

writelog 'but mysteriously, x="$x", y="$y", z="$z"'

When listening very carefully, you might hear the above example speak for itself. If not, you will find the key in the third line that contains the second readstr command. Did you notice the variables being prefixed by a dollar sign each? Now the dollar sign indicates STx that variable substitution is desired (and required) before the command is to be processed. So all STx does is replace the two strings "$#a", "$#b", and "$#c" by the contents of the respective variables. Since, at this stage, their contents are "x", "y", and "z", the command actually to get executed will look as follows:

readstr 'one two three' x y z

Now this clearly is a request to parse the string "one two three" into the three shell-global variables "x", "y", and "z".

When reading directly from one variable, you might prefer a variant of readstr, the readvar command. Readvar works similar to readstr, as the following example shows:

#var := set 'one two three'

readvar #var #a #b #c /Delete

The first argument to readvar is one variable to read from. The remaining arguments are the variables where to store the words read. Otherwise that readvar reads from a variable as opposed to reading from a literal string, there is no difference between readvar and readstr.

You will undoubtedly ask why there is such a thing as a readvar command. After all, all that

readvar #var #a #b #c

does may as well be done using the following command:

readstr '$#var' #a #b #c

You are, of course, right in principle. The difference between the two commands is that the latter depends upon variable substitution which introduces a second step when evaluating and executing the command. Hence, the former is simply faster.

Note that there is a readtable function, too. We will come to that much later when dealing with the versatile (both are) STx table feature.

Special Variables

There are a number of reserved variables that serve special purposes. At this stage, it suffices to give a short overview of the most important special variables.

Variable Description
rc After executing an S_TOOLS-STx statement or built-in function (not a user-defined macro!), this variable contains its numerical return code, 0 indicating success, and values different from 0 indicating different kinds of failure. "rc" gets set after each invocation of an STx statement or built-in function, meaning that an error code will get reset when executing the next statement.
emsg This variable contains a textual description of the value of the "rc" variable. It, too, gets reset with each new STx statement.
#argc, #argv On macro invocation, "argc" contains the number of arguments supplied to the respective macro, while "argv" contains the actual arguments (all of them). See below.
result After returning from a user-defined macro call, the variable "result" contains the value returned by the respective macro, i.e. the value the macro supplied as an argument to the "exit" call. If the macro was left without returning an argument, "result" is empty (this is not considered an error – honestly, there is not much that is ever considered an error as far as STx is concerned).
#read After using one of the S_TOOLS-STx commands readstr, readvar or readtab, this variable contains the number of arguments actually read.
#new When allocating an S_TOOLS-STx shell item, its item name will be stored in a variable called "#new". On the issue of shell items, please be patient until chapter 5 on page 1.

In general, even the reserved variable may be target of an assignment (this is sometimes used with the "#argv" variable for implementing default macro arguments). You should not be surprised, though, that assigning a value to either "rc" or "emsg" will not have the desired outcome: Since the assignment statement is built-in STx statement itself, executing it will reset "rc" to 0 and "emsg" to the empty string, thereby indicating that the assignment statement itself was successful (which it was).


<splist parent= sort=asc sortby=title showpath=no liststyle=unordered kidsonly=yes >

Navigation menu

Personal tools