If the goto graph implements a calculation with no side effects like I/O, my network will be all pure functions, too.
Completely beyond the reproach of any CS academic. :)
There is a mechanical way to do this.
1. We turn all of the function's local variables into function arguments. Each of our tail recursive functions will take these arguments.
2. Every labeled node becomes a function.
3. Every goto becomes a function call invoking the function that its target label was turned into, passing in each of its own argument to the corresponding parameter of that function. The function's return value is immediately returned.
4. Every variable assignment before a goto is turned into an argument expression in the function call which passes the new value to the corresponding argument. E.g. instead
a++; d *= 2;
goto foo;
we have return foo(a + 1, b, c, d * 2, e, ...);
Once you have this working, then you refactor. For instance, if you have a function like this: int foo(int a, int b, int c, ..., int z)
{
return a + c;
}
which does not pass its arguments to another tail-called function, then you can eliminate all of the arguments and just turn it into: int foo(int a, int c)
{
return a + c;
}
and of course edit all of the calls to foo accordingly, and simplify those places also. In this way, you will "discover" simpler functions that work with just a subset of the state transfer.However, the idea behind "goto considered harmful" is almost all code can be written with function, loops and if/else statements and get just as efficient, and much, much easier to understand.
What you can do with goto-based code is refactor it into a form that obeys certain conventions such that it is easy to mechanically translate to such a tail-call newtork, but then stop short of actually doing it.
Then when reading the code, just pretend you're looking at function calls.
I once made a poster (I wish I'd kept it) where I was tracing out the behaviour of a single mega function with around 50 labels in it, and gotos all over the place.
I worked on a reimplementation, slowly picking apart the function into subfunctions, while loops, recursive function calls, etc. Took me about 2 weeks.