Legato is a structured language, meaning it uses conventional code blocks contained within braces { }. The execution of these code blocks is controlled by statements such as while, for and if. In addition, functions can be declared as blocks, which of course, can contain additional blocks and functions.
Program flow is governed by five basic control methods:
Sequence — A sequence is a series of statements and functions executed in linear order. When grouped in braces, the sequence becomes a code block or, simply, a block. A sequence can be a compound statement, containing a number of operations and functions.
Function — A function is a named item with or without parameters that contains one or more code blocks. Functions appear in a sequence, including within statements. The function executes all code blocks until a return statement is encountered or the last statement in the sequence is executed.
Selection — A selection is a series of statements executed only if a condition is met. For example, an if statement may set a selection to execute on a true condition while the else statement executes the block on a false condition. The elseif statement allows for a series of tests to be conducted with one outcome. There is also the case statement, which is used with the switch statement.
Iteration — An iteration a series of statements that is executed repeatedly until some condition it met or the loop is broken using a break statement. In Legato, the for and while statements initiate a loop for iteration.
Recursion — Recursion is a statement that calls itself repeatedly until specified conditions are met. Recursions require a fair amount of stack space such that any recursive calls over about 100 should be avoided. Recursion is unique in that it allows for algorithms to be created that would otherwise be difficult to implement.
By convention, code within a block continues to execute until the end of a brace, a break or continue statement is encountered, or a return statement is encountered. A recursive process generally unwinds once complete by each function returning until the original function call has been completed.
The return Statement
In some respects, the return statement violates the tenants of structured programming. Generally, in pure structured programming, all code is structured in blocks as discussed above. Theoretically, this makes the code significantly more readable. Unfortunately, it also makes the program hard to maintain as adding a step often means fixing many indents. Additionally, dealing with errors becomes rather clumsy as it becomes harder to identify which individual part failed. To avoid this, some structured programming languages employ the try-throw-catch method, which allows for structured blocks with error checking, but still violates the purest notion of truly structured code. While in the end it is a matter of style and personal (or team) preference, maintainability and readability are generally more important considerations.
The return statement can be used to return a value from the function as part of the calling statement. It can also be used to escape a block of code. I frequently use return as part of error management, for example:
int open_source(string fnSource) {
int rc;
hFile = OpenFile(fnSource);
if (IsError(hFile)) {
rc = GetLastError();
ReportFileError(fnSource, rc);
return rc;
}
// do a bunch of stuff
rc = something();
if (IsError(rc)) { return rc; }
// do a bunch more stuff
rc = somethingelse();
if (IsError(rc)) { return rc; }
// do a even more stuff
return ERROR_NONE;
}
In a more structured manner, the code could be written:
int open_source(string fnSource) {
int rc;
hFile = OpenFile(fnSource);
if (IsError(hFile)) {
rc = GetLastError();
ReportFileError(fnSource, rc);
}
else {
// do a bunch of stuff
rc = something();
if (IsNotError(rc)) {
// do a bunch more stuff
rc = somethingelse();
if (IsNotError(rc)) {
// do a even more stuff
}
}
}
return rc;
}
One can see that the indent levels and grouping can get rather deep if the function has even a small amount of code. Another disadvantage to this method is that keeping track of the braces and levels can become complex and irritating.
The return statement also has an optional value. If the function definition specifies a data type, the return statement must return that type. If no data is returned, the function must be declared as void data type.
The Top Level Function
From the script engine’s standpoint, everything is a function. If your program is one line:
MessageBox("Hello World!");
The engine says, that is a default entry point, runs to the end of file and returns a default int value of 0. There are four methods to enter a script for Legato: default, “main” function, “entry” function, and a hook function.
Using the default method means there is no specified entry point. The script engine begins execution on the first global line of code. This method is useful for little scripts with few or no non-API functions. The default return data type is int.
The “main” and “entry” functions operate in the same manner. Using “main” is in line with traditional C and C++. The return data type for both is int. Most callers accept a return value of 0 as success and an error code as anything else.
Hooks are defined either by the programmer or the specific function being hooked. The return value and data type will vary depending on the application menu item or function being hooked.
The exit Statement
If you want to cut through the return path of all the functions on the stack, the exit statement is a good choice. It operates exactly like return except the stack is completely unwound. I like to use exit on flat scripts without a declared entry point. It indicates to me (or any other reader) this is a flat simple script.
Another case where exit may be appropriate is a significant failure on a recursion where you just want to leave the script. It may not be convenient to return up the call stack when you just want to bail out.
Finally
The unwind functions still get run, no matter how you exit the script.
As mentioned above and as a matter of style, I tend to use the non-structured ‘test for error and then return’ method as opposed to the ‘tortured structure’ method. In addition, the exit method seems more aesthetically pleasing on flat simple scripts. Otherwise, I always use the return method.
Scott Theis is the President of Novaworks and the principal developer of the Legato scripting language. He has extensive expertise with EDGAR, HTML, XBRL, and other programming languages. |
Additional Resources
Novaworks’ Legato Resources
Legato Script Developers LinkedIn Group
Primer: An Introduction to Legato