Welcome to part 4 of my Funge-98 tutorial. This part will cover program flow.
Changing Program Flow
Up til now everthing we have done the IP moved from one instruction to the next in a long sequence. There are serveral commands that can be used to alter how the IP moves to the next instruction.
We shall first look at the # instruction. Take a look at this code:
Here is how this program executes. First the '1' instruction will push a 1 onto the stack, the '2' instruction will push 2 onto the stack. Next we run into the '#' command. This command will jump over the cell that follows it and then continue execution. So in this case the '5' command is not executed since it was jumped over by the '#' command. The '+' now finds 1 and 2 on the stack which are added together to produce the 3 that gets printed.
There are 2 very common uses for the '#' command, first is to jump over an instruction stream that is perpenduclar to the currently execting stream. Here is an example of this:
v v < >1#.2+^ @
In this simple example we do not want to print the value 1 but addition of the 1 and 2 therefore we jump over the '.' command and return to it after we finished the calculation. The '#' command allowed us to jump over the '.' command instead of exectuing it.
The second major use of the '#' command is to catch reflections. There are commands that will reflect the IP when an error condition occurs. If you remember from an earlier lesson, reflection will change the IP delta to start moving in the opposite direction. In the case of detecting an error if the error causes a reflect the IP is going to want to go back over the code it was executing leading up to the error. There needs to be a way to trap the error reflection and act accordingly. Take a look at this program:
The first part of this program, '"XXXX"4' should be well understood by now. Next comes the '#' which will jump over the following 'v' command. Now the '(' command, this command is used to load a fingerprint (Which will be covered in a later lesson). The '(' command is one of those commands that will reflect on an error. Unless your interpreter knows the XXXX fingerprint, an error will result from the execution of the '(' command in this program, causing a reflection. Because of the error the IP is now travelling westward so the '0.@' is never executed. The next instruction to be executed is now the 'v' command which changes the IP to now execute our error handling code, which here does nothing more than print 1 and exit.
If you need to jump over more than 1 intervening cell then there are two methods in which it can be done. The first is by using the ';' command. Take a look at this program:
In this program, when the ';' command is found then the ip will continue travelling along its delta until another ';' is found and then the IP will be moved to the next instruction and executed. So in this program first 1 is pushed onto the stack and then the ';' is encountered, the IP moves along the line til the next ';' and then steps one more space finding the '3' command which is now executed.
There is an important side effect of using ';' commands to skip over cells and that is that the ';' command is not actually ever executed, It acts more like remark statements in other languages. So in this example, on the first cycle of the program the '1' is executed, on the second cycle the '3' command is executed, there are no intervening cycles between. This side effect really only becomes important when writing programs using concurrent funge and timing is critical between the IPs.
The other command that can be used to jump over more than one cell is the 'j' command. This command takes a number off of the stack and jumps over that number of cells. Lets take a look at an example:
1jvvvv'Ev > 'Av > 'B>,@ > 'C^ >'D^
Try to guess what this program will print before you run it.
Did you guess right? that it would print 'B'?
If you did not guess right, here is what happens. The '1' command put a 1 onto the stack, when 'j' executes it pops off the top of stack, in this case the 1. The IP is moved along its delta 1 cell, which actually places it at the first 'v' command. The 'j' command is now done. Just like in all other commands when a command is finished the IP is moved along its delta, which happens here and moves the IP to the second 'v' command. The rest you already know. Note, the '1j' commands do exactly the same as the '#' command.
Trying changing the '1' command to a '0' command and see that you get an 'A'. When zero if given to 'J' it does nothing at all, program flow will continue on as normal.
Now try '2'. Now when you run the program you get a 'C' as output.
The 'j' command here is being used as a selector, selecting wich path the program should take based upon the value taken from the stack.
Give this program a try:
3-4jvvvvj'Ev > 'Av > 'B>,@ > 'C^ > 'D^
Try guessing what this one will do before you run it.
Did you guess right, that it will print C?.
The 'j' command will accept a negative argument, in which case it jumps backwards along the IP's delta the specified number of cells. This program may have been a little tricky for you to follow so I will explain it step by step:
3- is executed first putting -3 onto the stack.
next 4j is executed leaving the IP pointing at the final 'v', after the command is executed it is moved forward to the next 'j' command.
The next j command now takes the next value from the stack, the -3 and jumps backwards 3 cells, landing on the 2nd 'v' command. Again once execution is complete the IP is moved forward along its delta, so now will be pointing at the third 'v' command. From this point you should already understand how the program will proceed, if not then review part 3 on the multi-dimensional commands.
Revisiting the 'k' command
I am going to finish up this part of the tutorial by going back to the 'k' command. Back in part 2 we learned that the 'k' command will execute the command following it a specified number of times. There are some important nuances to this command that are important to understand. Lets begin with this program:
Try guessing what this will do before you run it.
Did you guess right, that it will produce 0? Do you understand why? If so then you are really starting to understand this unique language. If you did not understand why, do not feel bad, many people have had considerable difficulty fully understanding the 'k' command.
Here is what happens in this program. First 123 will cause the stack to contain from bottom to top 1,2,3. Next 2 is pushed onto the stack and then 'k' is executed. Remember from part 2 that the 'k' command will now take the next command in the path of the IP, in this case the '*' command, which multiplies 2 numbers. 'k' will now execute this twice. The first time it will take the 2 and the 3 off the stack, multiply them together and push them back. Now the stack has 1,6. Now for the 2nd iteration, the '*' command will pop off the 6 and the 1, multiply them together getting 6 and pushing the result back onto the stack. The 'k' command is now done. So how did we end up with zero? Here is the important piece of the puzzle, the 'k' command does NOT move the IP when it executes the iterated command, the IP is pointing at the 'k' command not the command it is executing. Now when the 'k' command completes, like all other commands the IP is moved forward once along its delta, now pointing at the '*' command in the above program. The next command to execute is now the '*', which will take the 6 off of the stack, and since there are no more stack entries the other value that '*' will pop is 0. 6 times 0 is 0, which is pushed back onto the stack.
|Rule 1. Using the 'k' command will result in the following instruction to be exucted 1 more time past the iteration value.|
For every rule there is an exception and this rule of the 'k' command is no exception. Take a look at this program:
Based upon our rule above this would produce a 1 right? But does it?
There is a special case to the 'k' command and that is if the iteration value is zero then the next cell gets skipped alltogether, meaning 0k works exactly like the '#' command does. An imprtant ramification to this is that a command can be executed 0 times or 2 or more times by the 'k' command, it is not possible to specify an iteration that allows for the following command to only be executed once.
There is an exception to this as well, look at this program:
0k v0.@ >1.@
This program will produce 1 for output. The 0k causes the next cell to be skipped and not the next executable command, therefore in this program the 0k causes the blank following the k to be skipped and so now the 'v' command will execute. Using this work-around for the inability to iterate only once, now you cannot iterate zero times. This construct will now always execute 1 or more times.
|Rule 2. 0k will result in the same effect as if # was executed.|
The 'k' command will always get the next executable command in the path of the IP. Since the <space> character is not an executable command, any blank spaces are skipped. Look at this program:
This program will print 'Hello'. the 'k' went past all the spaces to find the ',' command to execute.
The 'k' command will also jump over any ';' blocks. Take a look at this program:
"olleH"4k ;@; ,@
Again when searching for a command to execute, the 'k' command skips anything between ';'s. Notice also that both ; blocks and spaces can be mixed and the 'k' command will still reach across them.
|Rule 3. k always skips spaces or ;-blocks when searching for an executable command to iterate|
It is also important to understand that iterated commands are executed where the 'k' command is and not where the executable command was found, take a look at this program:
2kv0.@ >1.@ > 2.@
This program will print 2, not 1. The 'k' command finds the 'v' command, executes it to turn the IP south from the position of the 'k' command.
|Rule 4. k always executes the iterated command at the cell where the k command is and not where the executable command was found|
There is one last trait of the 'k' command we need to look at. Examine this program:
3k#vvvv >0.@ > 1.@ > 2.@ > 3.@
This program will print a 1. Commands that modify the position of the IP, like the '#' command here do NOT reset the IP position to the 'k' before each iteration. So here is what happened with this program. The 3k initially starts at the position of the 'k' command. The command to be iterated is the '#' command. For iteration number 1, the IP is pointing at the 'k' command, the '#' causes the IP to be moved ahead once so now it is pointing at the '#'. Now the second iteration, the IP is still pointing at the '#' and now the '#' command adds one more to the IP's position, so now it is pointing at the first 'v'. Now for the third iteration, once again the '#' command moves the IP forward along its delta causing the IP to now be pointing at the 2nd 'v' command. Now the 'k' command is done and like all other instructions moves the IP forward along its delta to be pointing at the third 'v' command which is now the next command to be executed by the interpreter.
|Rule 5. If the IP is moved by the iterated command, each iteration of k will not reset the IP back to the position of the k command|
This concludes part 4 of the tutorial. Be sure you understand all the concepts in this tutorial before proceeding on to part 5. Part 5 will cover decision making.
|Command||What it does|
|#||Skip over 1 cell|
|;||Skip til the next ;|
|j||jump a specified number of cells|