Wednesday, March 9, 2016

What is Spaghetti Code?

When I posted the rant on my political blog about the body of US law loking too much like spaghetti code, I borrowed some examples from the Wikipedia article on spaghetti code.

Then I tried to find a basic interpreter that would run those archaic examples.

:)

Setting up an environment to run this kind of archaic code is a bit beyond an introductory topic.

So, to keep the original rant from getting too cluttered, I'm posting some more modern code for those examples in this post.

First, the example that was called structured on Wikipedia:



110 FOR i = 1 TO 10
120     PRINT i; " squared = "; i * i
130 NEXT i
140 PRINT "Program Completed."
150 END 


All you need to do to get this running in a more modern BASIC is remove the line numbers and maybe spread the print statement out:



FOR i = 1 TO 10
    PRINT i;
    PRINT " squared = ";
    PRINT i * i
NEXT i
PRINT "Program Completed."
END 


Then the example that was called spaghetti, but I think isn't really:



10 i = 0
20 i = i + 1
30 PRINT i; " squared = "; i * i
40 IF i >= 10 THEN GOTO 60
50 GOTO 20
60 PRINT "Program Completed."
70 END


You'll need some labels, which is probably why they called it spaghetti. Here's how it might look for a more modern BASIC:



i = 0
L20:
i = i + 1
PRINT i;
PRINT  " squared = ";
PRINT i * i
IF i >= 10 THEN GOTO L60
GOTO L20
L60:
PRINT "Program Completed.";
END


And my example of simple spaghetti:


200 e = 0
210 GOTO 300
220 PRINT i; " squared = "; i * i
230 GOTO 300
260 REMARK Remember to set e before you start! 
300 ON e GOTO 310,330
310 i = 0 
320 e = 1 
330 IF i < 10 THEN i = i + 1 ELSE GOTO 350
340 GOTO 220
350 PRINT "Program Completed."
360 END 


This can be a little trickier, because many BASIC interpreters differ about how the support the IF/THEN/ELSE constructs. Not to mention whether they support the computed GOTO. The following should run in a modern interpreter:



e = 0
GOTO L300
L220:
PRINT i;
PRINT " squared = ";
PRINT i * i
GOTO L300
REM Remember to set e before you start! 
L300:
IF e = 0 THEN GOTO L310
GOTO L330
L310:
i = 0 
e = 1 
L330:
IF i >= 10 THEN GOTO L350
i = i + 1 
GOTO L220
L350:
PRINT "Program Completed."
END 


I used Basic256 as my more modern interpreter rather than Gambas3 because using labels in Gambas3 just feels funny. Also, I would have had to talk about setting up an environment for the code to run in.

Gambas3 is actually useful for production code, where Basic256 is more for helping people learn BASIC programming skills (which is why Basic256 already has the environment set up).

Just for amusement, here's how these would look in C --

The clean version:



#include <stdio.h>
#include <stdlib.h>

/* Print the first ten squares the simplest way possible:
*/


int main( int argct, char * argvar[] )
{
   int i;

   for ( i = 1; i <= 10; i = i + 1 )
   {
      printf( "%d squared = %d\n", i, i * i );
   }
   puts( "Program Completed." );
   return EXIT_SUCCESS;
}


A version using GOTOs:



#include <stdio.h>
#include <stdlib.h>

/* Print the first ten squares the simplest way possible:
*/


int main( int argct, char * argvar[] )
{
   int i;

   i = 0;
l20:
   i = i + 1;
   printf( "%d squared = %d\n", i, i * i );
   if ( i >= 10 )
      goto l60;
   goto l20;
l60:
   puts( "Program Completed." );
   return EXIT_SUCCESS;
}


The spaghetti-fied version:



#include <stdio.h>
#include <stdlib.h>

/* Print the first ten squares the simplest way possible:
*/


int main( int argct, char * argvar[] )
{
   int e, i;

   e = 0;
   goto l300;
l220:
   printf( "%d squared = %d\n", i, i * i );
   goto l300;
   /* Remember to set e before you start! */
l300:
   if ( e == 0 )
      goto l310;
   goto l330;
l310: 
   i = 0; 
   e = 1; 
l330:
   if ( i >= 10 )
      goto l350;
   i = i + 1; 
   goto l220;
l350:
   puts( "Program Completed." );
   return EXIT_SUCCESS;
}


Here's what a session compiling the code and running it should look like --



me@mybox:~/work/spaghetti$ cc -Wall -o spaghetti spaghetti.c
me@mybox:~/work/spaghetti$ ./spaghetti
1 squared = 1
2 squared = 4
3 squared = 9
4 squared = 16
5 squared = 25
6 squared = 36
7 squared = 49
8 squared = 64
9 squared = 81
10 squared = 100
Program Completed.



The amusing thing about the C source code is that even the worst example here will compile and run correctly on any standard C compiler. But you can tell which version will be easier to figure out and add features to in the future.

Just for a certain kind of completeness, here's a FORTH version of the same function:



: tensquares 
   cr
   11 1 do 
       i . ." squared = " i i * . cr
   loop ;



Which you would invoke by entering tensquares at the FORTH command line, giving the same output as above.

Doing a version with the FORTH equivalent of GOTOs would be even more of a time waster than the rest of this rant, so I won't bother. It's probably even less interesting.