Get Rid of GoTo's
My (pseudo) code is full of goto's! What a mess... Let's change it into a more iterative code.
SPECIFICATION
Each input consists of:
<HEADER> {
0: <code>
1: <code> (optional)
2: <code> (optional)
...
99: <code> (optional)
}
where:
<HEADER>: It is the method signature, but it can be anything. Just repeat it in the output.
<code>: It is the main code that contains the unwanted goto's and need to be refactored. It is always numbered, starting at 0 and increasing sequentially. code can be one of:
- if [condition] goto [number] [trailing]: this is where the goto's are. The if clause contains any text as a condition, that should be repeated in the output. The number should be between 0 and 99, and should point to a valid line in the input. The trailing could be a semicolon, a text comment or even nothing at all, but it will not appear in the output.
- return: the return clause is special because it ends the method execution. In the input code, a return could be followed by others lines reachable by a goto. But in the output code, a return is never followed by a code line. The return can be followed by a semicolon or nothing at all.
- anything: anything else is just code clauses that should be repeated somewhere in the output code.
EXAMPLES
BEFORE:
method1() {
0: printMessage("This is the firstMethod");
1: if (inputString(0)) goto 3;
2: return;
3: setVar(10);
4: printMessage("Var is set to 10");
}
AFTER:
method1() {
0: printMessage("This is the firstMethod");
1: if (inputString(0)) {
2: setVar(10);
3: printMessage("Var is set to 10");
4: } else {
5: return;
6: }
}
This is the simplest example. The goto is changed into an if/else structure. The 'return' clause always ends a block (i.e., the code does not continue after it).
BEFORE:
public method2() {
0: addFlag(char, 1);
1: play(22, 3468, 4433);
2: int rndNum = random(3)
3: if(rndNum == 1) goto 6
4: if(rndNum == 2) goto 8
5: if(rndNum == 3) goto 10
6: addItem(36, 54, 0, 0);
7: return;
8: addItem(37, 54, 0, 0);
9: return;
10: addItem(38, 54, 0, 0);
11: return;
}
AFTER:
public method2() {
0: addFlag(char, 1);
1: play(22, 3468, 4433);
2: int rndNum = random(3)
3: if(rndNum == 1) {
4: addItem(36, 54, 0, 0);
5: return;
6: } else {
7: if(rndNum == 2) {
8: addItem(37, 54, 0, 0);
9: return;
10: } else {
11: if(rndNum == 3) {
12: addItem(38, 54, 0, 0);
13: return;
14: } else {
15: addItem(36, 54, 0, 0);
16: return;
17: }
18: }
19: }
}
This example is trickier. The three gotos end up creating nested if/else clauses. Also, as the transpiler can't know if rndNum(3) returns something from [1-2-3], there is also an ultimate else clause afterwards.
BEFORE:
public method3() {
0: int curChar = whoMenu("Test", "Super");
1: if (firstChar(currentChar, 1)) goto 5;
2: printMessage("The sun is hot.");
3: if (YesNo(1)) goto 17;
4: return;
5: addVar(currentChar, 10);
6: addVar(currentChar, 20);
7: addVar(currentChar, 30);
8: addVar(currentChar, 40);
9: addVar(currentChar, 50);
10: addVar(currentChar, 60);
11: addVar(currentChar, 70);
12: addVar(currentChar, 80);
13: addVar(currentChar, 90);
14: addVar(currentChar, 100);
15: printMessage("You feel good");
16: if (YesNo(1)) goto 17;
17: addFlag(currentChar, 82);
}
AFTER:
public method3() {
0: int curChar = whoMenu("Test", "Super");
1: if (firstChar(currentChar, 1)) {
2: addVar(currentChar, 10);
3: addVar(currentChar, 20);
4: addVar(currentChar, 30);
5: addVar(currentChar, 40);
6: addVar(currentChar, 50);
7: addVar(currentChar, 60);
8: addVar(currentChar, 70);
9: addVar(currentChar, 80);
10: addVar(currentChar, 90);
11: addVar(currentChar, 100);
12: printMessage("You feel good");
13: if (YesNo(1)) {
14: addFlag(currentChar, 82);
15: }
16: } else {
17: printMessage("The sun is hot.");
18: if (YesNo(1)) {
19: addFlag(currentChar, 82);
20: } else {
21: return;
22: }
23: }
}
This example is not so hard, but note there are two goto's going to the same line. As this line does not create a loop, it is permitted (see rules below).
BEFORE:
method4() {
0: printMessage("This is the firstMethod");
1: if (inputString(0)) goto 4;
2: printMessage("End is near.");
3: return;
4: setVar(10);
5: if (inputString(1)) goto 2;
6: printMessage("Var is set to 10");
}
AFTER:
method4() {
0: printMessage("This is the firstMethod");
1: if (inputString(0)) {
2: setVar(10);
3: if (inputString(1)) {
4: printMessage("End is near.");
5: return;
6: } else {
7: printMessage("Var is set to 10");
8: }
9: } else {
10: printMessage("End is near.");
11: return;
12: }
}
This example is also tricky, because it contains a goto to a previous line. This does not create a loop, but it also creates a block that is repeated twice in the code.
CONSIDERATIONS:
1) Goto's are always associated with an 'if' clause. Replace the goto with a start block '{' clause.
2) Goto's always create an 'if', but not necessarily an 'else'.
3) Some clauses can repeat due to more than one goto's going to the same line (directly or not).
4) There won't be loops. Example of invalid code:
public methodInvalid() {
0: if(YesNo(1)) goto 2;
1: printMessage("this code is invalid.");
2: if(NoYes(1)) goto 0;
3: printMessage("still invalid.");
}
5) Identation in the output is desirable. Each if/else block must be nested with a tab for each nested level. If you want to replace the tab for 2, 3 or 4 spaces, ok, just state it and make it coherent on your code.
6) As there won't be more goto's, the line numbers are optional in the output code. Just don't put them out of order, if you want to keep them.
7) The first line can be ignored (just repeat it in the output) and the last line is always the close statement ('}'). These lines are never numbered.
8) The input will always contains line numbers, starting with 0 and a colon. The next line will be 1 and a colon, and so on. No more than 99 lines will be present.
9) There is also at least one space (or tab or opening parenthesis) after the if statement, one space before the goto statement and one space before the number after it. So "1:ifagoto2" is invalid, but "1:if a goto 2" is valid and also valid is "1: if(a) goto 2". Spaces (or tabs) are optional before the line numbers and between the semicolon and the start of the code.
10) You only need to worry with (a) 'goto's; (b) line numbers; (c) the 'return' clause; and (d) the end of the code (last line). Everything else is gibberish, you don't need to interpret the code. See a valid input below:
BEFORE:
fun with code {
0: gibberish
1: if a goto 3
2: gibberish
3: gibberish
4: gibberish
}
AFTER:
fun with code {
0: gibberish
1: if a {
2: gibberish
3: gibberish
4: } else {
5: gibberish
6: gibberish
7: gibberish
8: }
}
Standard loopholes apply, shortest answer in bytes wins!