Date: Fri, 12 Feb 1999 01:07:40 -0700 From: "Sean M. Burke" Subject: life without goto In the off chance you're translating impenetrable goto-filled code into a language with no goto, these approaches to gotolessness might be helpful: Consider code like this, that one simply has to translate into a gotoless langauge: [program starts] Label10: ...statements... Label20: ...statements... if CONDITION1 goto Label10 if CONDITION2 goto Label30 ...statements.... Label30: ...statements.... [program ends] There are several possibilities: 1) Don't translate. Either: a) simply write, in the target language, an interpreter for your source language. This is usually impractical. b) Understand the algorithm that the code implements, and reimplement it from scratch in code that doesn't have gotos. This is a mentally taxing process. The following possibilities are less so -- they're straightforward and don't involve total reimplementation. 2) Find another mechanism in the target language that can be kludged into emulating goto. For example, if a language in question has event handlers, !AND! if a handler can trigger an arbitrary event that, ONCE the current handler has stopped executing, will cause another (arbitrary) handler to be called, then you have something that can made to serve as a goto mechanism. You can do this with functions, without having to assume these things about handlers, altho there are some limitations: 3) A special case of #2: The vacuous function approach. Asssuming that, for the sake of notation in this discussion: a) the target language defines functions (procedures, routines, etc.) in the form sub functionname { ...statements...} b) you call a function by saying: functionname(...parameters...) or functionname() if you are passing no parameters. These calling forms are expressions whose value is the return value of that function. c) inside a function, you return a return value by saying: "return EXPRESSION", (where EXPRESSION is anything that can result in a value, including possibly a call or another function). In that case, you can simply translate the code from above as: sub function10 { # was Label10 ...statements... return function20; # fall thru to function20 } sub function20 { # was Label20 ...statements... if CONDITION1 return function10Label10(); if CONDITION1 return function10Label30(); ...statements.... return function10Label30(); } sub function30 { # was Label30 ...statements.... return; # to halt } To get the ball rolling, you just call function10(). These are somewhat cockeyed as functions because all the calling around is done just for the effects the "...statements..." in the functions have, not for return values the functions return. But that's just a matter of aesthetics. A limitation of this approach is that this fails on code that doesn't halt. Consider: Label10: print "Hello"; Label20: goto Label10; which repeats forever and ever. That'd translate as: sub function10 { print "Hello"; return function20(); } sub function20 { return function10; } But in implementions of functions in all languages I'm familiar with, this would start to run forever, then overflow the stack, and halt. But as long as no blocks in the original code are nonterminating, the limitation is not a problem. 4) The "state machine" approach. This can be implemented several ways that all do the same thing. Here's one: Assume a language that provides a "while CONDITION { ...statements... }" structure for loops. Assume that, in there, you can say "next", to mean "now start a new iteration of this loop structure". In that case, this code from above: [program starts] Label10: ...statements... Label20: ...statements... if CONDITION1 goto Label10 if CONDITION2 goto Label30 ...statements.... Label30: ...statements.... [program ends] would translate as: $Target_action = "action10"; while($Target_action != "STOP") { if($Target_action == "action10") { # was Label10 ...statements... # fall thru via next iteration $Target_action = "action20"; next; } if($Target_action == "action20") { # was Label20 ...statements... if CONDITION1 { # was a "goto Label10" $Target_action = "action10"; next; } if CONDITION2 { # was a "goto Label30" $Target_action = "action30"; next; } ...statements... # fall thru via next iteration $Target_action = "action30"; next; } if($Target_action == "action30") { # was Label30 ...statements... # fall thru to halting execution $Target_action = 'STOP'; next; } } Using that model, this: Label10: print "Hello"; Label20: goto Label10; would translate as: $Target_action = "action10"; while($Target_action != "STOP") { if($Target_action == "action10") { # was Label10 print "Hello"; # fall thru via next iteration $Target_action = "action20"; next; } if($Target_action == "action20") { # was Label20 $Target_action = "action10"; next; # fall thru to halting execution $Target_action = 'STOP'; next; } } of course, that does just loop forever and ever -- that code that sets Target_action to "STOP" never gets executed. If the target language provides nothing that works like "next", then you can get around it, somewhat messily. Consider: Label50: ...statements... if CONDITION3 goto Label70 ...statements... [falls thru to label60] That could translate, without using nexts, as: if($Target_action == "action50") { # was Label50 ...statements... if CONDITION3 { # was a "goto Label70" $Target_action = "action70"; } else { # everything that would have followed the next: ...statements... # fall thru via next iteration $Target_action = "action60"; } } So consider the code from further above: Label20: ...statements... if CONDITION1 goto Label10 if CONDITION2 goto Label30 ...statements.... You could translate it as: if($Target_action == "action20") { # was Label20 ...statements... if CONDITION1 { # was a "goto Label10" $Target_action = "action10"; } else { if CONDITION2 { # was a "goto Label30" $Target_action = "action30"; next; } else { ...statements... # fall thru via next iteration $Target_action = "action30"; } } } So not having a "next" is messy, but still tractable. But if you don't have a loop construct in the target language, that's a big problem. Something might be fakeable with, in BASIC terms: for i = 1 to 10; i = i - 1 ...stuff... next i; -- sburke@cpan.org