The "If" command can be used to conditionally skip some commands. The "If" command has 1 parameter, the condition. An expression is usually supplied. Following the "If" command, the command(s) to be conditionally skipped should appear, followed by an "EndIf" command that indicates the end of the "If" section.
If the condition evaluates to true, then the commands within the "If" section will be executed. If the condition evaluates to false, then the commands within the "If" section will be skipped.
In the following example, the 2 "Set" commands will only be executed if the value of the variable "a" is equal to 5. If it is not equal to 5, the 2 "Set" commands are skipped (not executed).
If a == 5;
Set a, 6;
Set b, 11;
EndIf;
Using the "Else" command, it is possible to provide alternate commands that will be executed in the event of the condition evaluating to false. In the following example, if "a" is equal to 5, then "b" is set to 11, but if "a" is NOT equal to 5, then "b" is set to 66. Only 1 of the 2 "Set" commands will be executed, depending on whether "a" equals 5.
If a == 5;
Set b, 11;
Else;
Set b, 66;
EndIf;
Using the "ElseIf" command, it is possible to provide alternate conditions (and associated alternate commands to be executed). If the condition in the "If" command evaluates to false, then the next "ElseIf" command will be executed, and if the condition in the "ElseIf" command evaluates to true, then the commands associated with the "ElseIf" command will be executed.
In the following example, if "a" equals 5, then "b" is set to 11. If "a" does NOT equal 5, then it checks whether "a" equals 7, and if it does, then "b" is set to 22. If "a" does NOT equal 7 (and does not equal 5), then "b" is set to 66. Only 1 of the 3 "Set" commands will be executed.
If a == 5;
Set b, 11;
ElseIf a == 7;
Set b, 22;
Else;
Set b, 66;
EndIf;
The "Else" command is optional, but if it is present in an "If" sequence then there can be no more than 1 and it must be last (after all/any "ElseIf" commands).
Multiple "ElseIf" commands can be present in an "If" sequence, as in the following example. The first "If" or "ElseIf" with a condition that evaluates to true will have its associated commands executed. The conditions are evaluated sequentially in the same order as they appear (top to bottom). Thus in the following example, if "a" equals 30, then 3 comparisons/checks are performed ("a == 10", then "a == 20", then "a == 30").
{Assume "a" equals 30.}
If a == 10; {EXECUTED}
Set a, 1111; {NOT EXECUTED}
ElseIf a == 20; {EXECUTED}
Set a, 2222; {NOT EXECUTED}
ElseIf a == 30; {EXECUTED}
Set a, 3333; {EXECUTED}
ElseIf a == 40; {NOT EXECUTED}
Set a, 4444; {NOT EXECUTED}
ElseIf a == 50; {NOT EXECUTED}
Set a, 5555; {NOT EXECUTED}
EndIf;
The "==" operator/function tests whether 2 values are equal. It is also possible to test whether a value is less than another value, etc. The main operators are summarized below. For more information, see the Expressions section of this documentation.
| Name | Symbol | Description |
|---|---|---|
| Equal | == | If the 2 values are equal. |
| NotEqual | != | If the 2 values are not equal. |
| Less | < | If the first value is less than the second value. |
| Greater | > | If the first value is greater than the second value. |
| LessEqual | <= | If the first value is less than or equal to the second value. |
| GreaterEqual | >= | If the first value is greater than or equal to the second value. |
| And | && | If the 2 values are both true. |
| NotAnd | !&& | If the 2 values are not both true. |
| Or | || | If either of the 2 values are true (or both). |
| NotOr | !|| | If neither of the 2 values are true. |
To write a condition for an "If" command that is satisfied (true) if either of 2 other conditions are true, use the "Or" operator/function. In the following example, "q" will be set to 123 if "a" is equal to "b", or if "x" is equal to "y".
If (a == b) || (x == y);
Set q, 123;
EndIf;
In the following example, "q" is set to 123 if "n" is in the range 100 to 200 (inclusive), or if "n" is zero.
If ((n >= 100) && (n <= 200)) || (n == 0);
Set q, 123;
EndIf;
Note that expressions are evaluated simply left-to-right. Therefore, the following 2 commands are equivalent:
If a == b && x == y; If ((a == b) && x) == y;
Here is a Truth Table for the logical operators, displaying the result of "a $ b" where "$" is one the logical operators such as "AND":
| a | b | AND | OR | NAND | NOR | XOR |
|---|---|---|---|---|---|---|
| False | False | False | False | True | True | False |
| False | True | False | True | True | False | True |
| True | False | False | True | True | False | True |
| True | True | True | True | False | False | False |
"If" commands can be nested, meaning an "If" sequence can appear inside another "If" sequence (either alone or amongst other commands). The nested inner "If" sequence operates in exactly the same manner as if it were not nested. However, the outer "If" sequence controls whether the inner "If" sequence will be executed. For example:
If n < 0; {if "n" is negative}
If n < -1000;
DoSplendidThings;
EndIf;
Set n, -1;
Else; {"n" is positive}
If n > 1000;
DoWonderfulThings;
EndIf;
Set n, 1;
EndIf;
When using a nested "If" sequence, ensure that you do not forget to provide an "EndIf" command for both the nested inner "If" sequence and the outer "If" sequence. Accidentally omitting an "EndIf" command can result in a confusing/ambiguous/invalid interpretation.
"If" commands can be nested to at least 10 levels deep.
The "IfNot" command operates the same as the "If" command except with the opposite interpretation of the boolean condition. The following 2 examples are equivalent. The second uses the function "Not".
IfNot condition;
{...}
EndIf;
If Not[condition];
{...}
EndIf;
The "If" and other commands have shortcut names that are easier to type. You can use either the full name or the shortcut name.
| Name | Shortcut |
|---|---|
| If | if |
| IfNot | ifnot |
| ElseIf | elif |
| Else | else |
| EndIf | eif |
Here is an example using shortcut names:
if a == 5;
set b, 11;
elif a == 7;
set b, 22;
else;
set b, 66;
eif;
The "GoTo" (shortcut "Goto" and "goto") command can be used to cause execution of the program to jump to (go to) a different location within the current Command Implementation, either forwards or backwards. The goto command has 1 parameter, the name of the location to jump to. The location is separately defined using the "Location" (shortcut "loc") command. The "Location" command has 1 parameter, a name to assign to this location (the location is where the "Location" command is). For example:
GoTo test;
Set a, 123; {this is skipped}
Location test; {the destination}
The "goto" command cannot be used to jump to a location in a different Command Implementation. The "goto" command and its corresponding named destination location must be within the same Command Implementation, with either appearing first.
There can be multiple "goto" commands in different places (within the same Command Implementation) that jump to the same named destination location. There cannot be 2 locations with the same name. Location names have the same restrictions as variable names (no spaces, symbols, etc).
Some people have claimed that "goto" commands/statements in programming languages are harmful and should not be used. This nonsense originates from a letter written to a magazine/journal by a man named Edsger Dijkstra in 1968:
"Letters to the editor: go to statement considered harmful" by Edsger W. Dijkstra, Communications of the ACM, Volume 11, Issue 3 (March 1968), pages 147-148.
However, Dijkstra issued handwritten manuscripts and rarely used computers, and owned only 1 computer late in his life, and did not do any programming on it. He never did any real programming (meaning he never created a real-world software product used for real-world non-theoretical tasks).
"Dijkstra eschewed the use of computers in his own work for many decades. Even after he succumbed to his UT [University of Texas] colleagues' encouragement and acquired a Macintosh computer, he used it only for e-mail and for browsing the World Wide Web. He had no use for word processors, believing that one should be able to write a letter or article without rough drafts, rewriting, or any significant editing."
("In Memoriam of Edsger Wybe Dijkstra" by Jayadev Misra and Hamilton Richards, University of Texas at Austin, 3 Nov 2003.)
Certainly "goto" can be abused, but so can kitchen knives. That is no reason to ban kitchen knives. Almost anything in a programming language can be abused. If we removed all the features that can be abused, there would be nothing remaining in the language.
It is clear to experienced competent software engineers involved in the production of real-world software that "goto" is useful and helpful in some situations, and is only harmful if abused or used excessively.
The command named "Return" ("return") causes the current Command Implementation or Function Implementation to terminate its execution, and return to the invoker (the invoker is the Command/Function Implementation that invoked the current Command/Function Implementation).
When used within a Command Implementation, the "Return" command has no parameters. When used within a Function Implementation, the "Return" command has 1 parameter. See the Function Implementations section of this documentation for more information.
The "Return" command may be invoked multiple times in different places within the Command/Function Implementation. After a "Return" command is executed, no more commands in the Command/Function Implementation are executed.
If execution reaches the end of a Command Implementation, it returns as if there were a "Return" command at the end. Thus the "Return" command is used to initiate an early return.
Using the "Return" command is equivalent to jumping the execution to the end of the current Command Implementation using the "GoTo" command. Consider this example:
Command Test;
DoSplendidThings;
Return;
DoWonderfulThings;
EndCommand;
That is equivalent to:
Command Test;
DoSplendidThings;
GoTo theEnd;
DoWonderfulThings;
Location theEnd;
EndCommand;
One or more commands can be executed repeatedly using the "Repeat" (or "repeat") command. The commands appearing between the "Repeat" command and the "EndRepeat" (shortcut "erepeat") command will be repeated. Following is an example of an infinite loop (never-ending repetition):
Variable msg, TText;
Variable counter, TUInt, 0;
Repeat;
Increment counter; {add 1 to counter}
MText.Set msg, "Never say die ";
MText.AppendDecInt msg, counter;
MCLI.Output msg;
EndRepeat;
To exit/abort a repetition from inside the repetition, either the "GoTo" or "Break" command can be used. The "Break" (or "break") command causes execution to jump to the command after the "EndRepeat" command. For example:
Variable msg, TText;
Variable sq, TUInt, 2;
Repeat;
MText.Set msg, "sq contains ";
MText.AppendDecInt msg, sq;
MCLI.Output msg;
If sq == 65536;
Break;
EndIf;
Set sq, sq * sq;
EndRepeat;
MCLI.Output "The end.";
To repeat a certain number of times (to perform a certain number of iterations), supply a count for the first parameter of the "Repeat" command. The following example performs the commands 5 times:
Variable n, TUInt;
Set n, 3 + 2;
Repeat n;
MCLI.Output "I have MPD.";
MCLI.Output "No I don't!";
EndRepeat;
In the above example, the variable "n" is not modified by the "Repeat" command, and any modification of "n" within the repeat section (during the repetition) does not change the number of repetitions performed.
The count supplied to the "Repeat" command can be zero. In that case, the commands within the repeat section are not executed. (Whereas if the count parameter is omitted entirely, the repetition count defaults to infinity.)
The first parameter of the "Repeat" command (the repetition count) can be an expression. The expression is calculated once when the repetition starts, not recalculated with each iteration. The repetition count is typecast to an unsigned integer. If it is negative, an error is generated.
"Repeat" commands can be nested, meaning a repeat section can be inside another repeat section. For example:
Variable rowCount, TUInt, 3;
Variable columnCount, TUInt, 6;
Variable msg, TText;
Repeat rowCount;
MText.AppendChar msg, '|';
Repeat columnCount;
MText.AppendChar msg, '*';
EndRepeat;
MText.AppendChar msg, '|';
MText.AppendLineBreak msg;
EndRepeat;
MCLI.Output msg;
That produces the following output text:
|******| |******| |******|
When using nested repeats, multiple repeat sections can be aborted simultaneously by providing a parameter to the "Break" command -- a constant number indicating the number of repeat sections to abort (if no parameter is supplied, it defaults to 1). For example:
Repeat 10;
Repeat 15;
Repeat 25;
Break 2;
MCLI.Output "Innermost"; {NOT EXECUTED}
EndRepeat;
MCLI.Output "Middle"; {NOT EXECUTED}
EndRepeat;
{The "break 2" jumps to here.}
MCLI.Output "Outermost"; {EXECUTED}
EndRepeat;
To end the current iteration early and immediately begin the next iteration, use the "Continue" (or "continue") command. It causes execution to jump to the end of the repeat section (but without aborting the repetition) and from there execution will return to the beginning of the repeat section for the next iteration (unless the current iteration is the last iteration). It does not change the total number of iterations performed, it affects only the current iteration. Example:
Repeat 10;
MCLI.Output "Apples"; {EXECUTED}
Continue;
MCLI.Output "Oranges"; {NOT EXECUTED}
EndRepeat;
That is equivalent to the following:
Repeat 10;
MCLI.Output "Apples"; {EXECUTED}
GoTo bottom;
MCLI.Output "Oranges"; {NOT EXECUTED}
Location bottom;
EndRepeat;
A "Repeat" command is equivalent to a certain arrangement of "GoTo" commands. Consider this example:
Repeat n * 3;
MCLI.Output "Say hello";
MCLI.Output "to my little friend";
EndRepeat;
That is equivalent to the following:
Variable countDown, TUInt; Set countDown, n * 3; Goto bottom; Location top; MCLI.Output "Say hello"; MCLI.Output "to my little friend"; Location bottom; GotoIfDecr countDown, top;
Any commands appearing between a "While" (or "while") command and an "EndWhile" (shortcut "ewhile") command will be repeated while a condition expression evaluates to true (repeated until the expression evaluates to false). The expression is recalculated with each iteration of the While/EndWhile section.
The first (and only) parameter of the "While" command is the condition expression. The following example displays the text "1 2 3 4 5 ".
Variable msg, TText;
Variable x, TUInt, 1;
While x <= 5;
MText.AppendUInt32 msg, x, kDecimal;
MText.AppendSpace msg;
Increment x;
EndWhile;
MCLI.Output msg;
That is equivalent to the following:
Variable msg, TText; Variable x, TUInt, 1; Location top; GotoIfNot x <= 5, bottom; MText.AppendUInt32 msg, x, kDecimal; MText.AppendSpace msg; Increment x; Goto top; Location bottom; MCLI.Output msg;
Or to:
Variable msg, TText; Variable x, TUInt, 1; GotoIfNot x <= 5, bottom; Location top; MText.AppendUInt32 msg, x, kDecimal; MText.AppendSpace msg; Increment x; GotoIf x <= 5, top; Location bottom; MCLI.Output msg;
The compiler converts the "While" command to either of the above sequences. The compiler decides which to use. This may be affected by whether it is optimizing for speed or size. The result/effect of the program is the same in either case.
If the condition parameter of the "While" command is omitted, then it must be supplied with the "EndWhile" command instead. This changes the behavior slightly, causing the commands within the While/EndWhile section to be executed at least once regardless of the condition expression.
In other words, if the condition expression is supplied to the "While" command, then it is evaluated BEFORE the commands in the While/EndWhile section, whereas if the condition expression is supplied to the "EndWhile" command, then it is evaluated AFTER the commands in the While/EndWhile section.
The following example compares the 2 forms of While/EndWhile sections, using the same condition expression and starting state. In this example, the condition expression "x == 500" never evaluates to true.
Variable x, TUInt, 0;
{At this point, x contains zero.}
While x == 500;
Increment x; {Not executed.}
EndWhile;
{At this point, x still contains zero.}
While;
Increment x; {Executed once.}
EndWhile x == 500;
{At this point, x contains 1.}
In the following example, if "y" is 3, then the text displayed is "3 2 1 " and "x" contains 0 at the end. If "y" is 0, then the text displayed is "0 " and "x" contains -1 at the end.
Variable msg, TText;
Variable x, TSInt; {signed integer}
Set x, y;
While;
MText.AppendUInt32 msg, x, kDecimal;
MText.AppendSpace msg;
Decrement x;
EndWhile x > 0;
MCLI.Output msg;
That is equivalent to the following:
Variable msg, TText;
Variable x, TSInt; {signed integer}
Set x, y;
Location top;
MText.AppendUInt32 msg, x, kDecimal;
MText.AppendSpace msg;
Decrement x;
GotoIf x > 0, top;
MCLI.Output msg;
**** MORE INFO TO BE WRITTEN ****
This proposed feature allows multiple values to be used with a "switch" command. If you have nested switch commands (switch commands inside the cases of a switch commands, or if/else commands inside the cases of a switch command), then you might use a multi-value switch instead. For example, consider this example with nested switch commands:
switch a;
case 1;
switch b;
case 60;
{...}
case 70;
{...}
eswitch;
case 2;
switch b;
case 880;
{...}
case 881;
{...}
eswitch;
case 3;
switch b;
case 70;
{...}
case 80;
{...}
eswitch;
eswitch;
It could be rewritten as a multi-value switch like this:
switch a, b;
case 1, 60;
{...}
case 1, 70;
{...}
case 2, 880;
{...}
case 2, 881;
{...}
case 3, 70;
{...}
case 3, 80;
{...}
eswitch;
Consider the following example using if/else and goto commands.
if b && c;
goto doX3;
eif;
if a;
set x, 1;
elif b || c;
set x, 2;
else;
loc doX3;
set x, 3;
eif;
It could potentially be rewritten as a multi-value switch like this:
switch a, b, c;
case true, false, false;
case true, false, true;
case true, true, false;
set x, 1;
case false, true, false;
case false, false, true;
set x, 2;
default;
set x, 3;
eswitch;
When using a complex if/else sequence, sometimes it is good to separate the actions/results from the logic testing of the conditions, especially when there are multiple conditions that should be directed to the same lengthy chunk of code.
In the following example, the actions are not performed within the complex if/else sequence. Instead the if/else sequence merely determines which action to perform, and then the lengthy action is performed after the entire if/else sequence using a switch to perform only the selected action.
if b && c;
set action, 3;
elif a;
set action, 1;
elif b || c;
set action, 2;
elif !d;
set action, 1;
else;
set action, 3;
eif;
switch action;
case 1;
{...}
case 2;
{...}
case 3;
{...}
eswitch;
The conditional goto commands perform a goto operation only if a certain condition is satisfied. They have 2 parameters. The first parameter is the value, variable, condition, or expression. The second parameter is the name of the destination location.
| Command | Description |
|---|---|
| GotoIf | "Goto if value is true". Execution jumps to the specified location if the value is true. The value must be a boolean. |
| GotoIfNot | "Goto if value is not true". Execution jumps to the specified location if the value is false. The value must be a boolean. |
| GotoIfEQZ | "Goto if value is equal to zero". Execution jumps to the specified location if the value is equal to zero. |
| GotoIfNEQZ | "Goto if value is not equal to zero". Execution jumps to the specified location if the value is not equal to zero. |
| GotoIfDecr | "Goto if variable was decremented". The variable is decremented if it is greater than zero. In other words, check if the variable is greater than zero, and if it is, then subtract 1 from it and goto the specified location. If the variable is zero or negative, it is not changed and the goto is not performed. It is impossible for this command to cause arithmetic overflow or underflow. |
A decompiler or disassembler may emit a "GotoIf", or "GotoIfDecr", etc., command instead of trying to reconstruct/guess the original higher-level "If", "Repeat", or other command. (A decompiler is a utility that converts compiled code back to source code, or rather compiled code to degraded source code.)
Following is an example of using the "GotoIf" command:
loc test;
{...}
GotoIf a == b, test;
That is equivalent to:
loc test;
{...}
if a == b;
goto test;
eif;
Following is an example of using the "GotoIfEQZ" command:
loc test;
{...}
GotoIfEQZ n, test;
That is equivalent to:
loc test;
{...}
if n == 0;
goto test;
eif;
Consider this if/else sequence of commands:
if n == 1;
DoInterestingThings;
elif n == 2;
DoGreatThings;
else;
DoSplendidThings;
eif;
That is equivalent to:
GotoIfNot n == 1, secondTest; DoInterestingThings; goto theEnd; loc secondTest; GotoIfNot n == 2, defaultSection; DoGreatThings; goto theEnd; loc defaultSection; DoSplendidThings; loc theEnd;