Each statement in an S_TOOLS-STx script may be labelled. The name of a label may consist of letters, digits, and underscores (though it must not start with a digit). The label must be immediately followed by a colon (that's necessary because otherwise there would be no way to discern between labels and macro calls). Note that labels are local to the macro they are part of. It is hence not possible to jump to a label that is part of a macro different from the executing macro (and it is possible to use labels of the same name in more than one macro).
When using a label, you may at any time divert the flow of control to that label. This is done with the "goto" command. Though it has come out of fashion recently, the "goto" command allows for very efficient diversions in the control flow that may be really hard to follow.
Contrary to most other programming languages, S_TOOLS-STx allows "goto" to be supplied not only one, but even two labels. If this is the case, S_TOOLS-STx first tries to go to the first label. Only if there happens to be no such label, S_TOOLS-STx falls back to the second label and tries to go there. If the second label is not present either, the "goto" command will do nothing (it will leave an error code in the variable rc, though more specifically, an error code 10 , but most programmers do not check for errors anyway).
The following loop will count from 1 to 10 (compare this with the "for" loop of the last example in the previous chapter):
#a := int 0
if '$#a' >= 10 goto endloop
#a := int $#a + 1
writelog 'at this stage, a=$#a'
writelog 'let it be'
A different issue is the "gosub" command. It is very similar to calling a macro: A new execution environment will be created, and control will resume at the label referred to by the "gosub" command. When (and if) the control flow reaches an "exit" command, this execution environment will be destroyed, and control will resume with the line immediately following the "gosub" command that caused all that to happen. Summarizing things up, the code called by the "gosub" statement will see all variables be empty. Furthermore, any changes made during the statements invoked using "gosub" will get lost on returning. This is a very powerful feature.
The "gosubx" statement differs from the "gosub" command in that no separate execution environment is being created. Hence, the code will see all variables having their current value, and, conversely, any changes in variables done by the called code will remain in effect after returning. This, too, is always a powerful, often a desired feature, and seldom easy to follow.
Compare the effect of the "gosub" and the "gosubx" commands in the following example:
#a := 5
writelog 'a after gosubbing subroutine: $#a (no change!)'
writelog 'a after gosubxing subroutine: $#a (changed!)'
// should #a be unset, numerical evaluation will fail
#a := int $#a + 5
writelog 'value of a in subroutine: $#a'
Note that we do not actually dare to recommend using any of these statements, and that we strongly encourage structured programming (using "for" and "while" loops and breaking program code into macros). There may, however, be cases where the real programmer finds that he or she can do many interesting things with the help of the statements considered harmful since Dijkstra's famous letter.