【问题标题】:Automated GOTO removal algorithm自动 GOTO 删除算法
【发布时间】:2013-09-25 22:14:20
【问题描述】:

我听说理论上已经证明可以在图灵完备的语言中仅使用结构化编程结构(条件、循环和循环中断以及子程序调用)来表达任何控制流,而无需任何任意 GOTO陈述。有什么方法可以使用该理论自动将包含GOTOs 的代码重构为不包含GOTOs 的代码?

假设我有一个使用简单命令式语言(例如 C 或 Pascal)的任意单个子例程。我还有一个解析器,它可以验证这个子例程是否有效,并从中生成一个抽象语法树。但是代码包含GOTOs 和标签,它们可以向前或向后跳转到任意点,包括进出条件或循环块,但不能跳出子程序本身。

是否有一种算法可以将此 AST 重新加工成语义相同但不包含任何标签或GOTO 语句的新代码?

【问题讨论】:

  • 但是汇编器会用(有条件的)跳转再次替换它!
  • @wildplasser:汇编器无关紧要。我正在尝试将代码从支持 GOTO(并且在其中经常使用)的旧语言转换为不支持 GOTO 但与其他语言相比具有许多技术优势的新语言。我已经可以完成 99% 的工作,但我不知道如何重构 GOTO。
  • @irrelephant:我看到了,但它的范围特别限于仅向前跳跃。我的情况不是,所以不是重复的。
  • 提示:如果您需要指标变量,您应该停止。

标签: algorithm language-agnostic goto


【解决方案1】:

原则上,这样做总是可行的,尽管结果可能不太好。

始终消除 goto 的一种方法是按以下方式转换程序。首先对原始程序中的所有指令进行编号。例如,给定这个程序:

start:
    while (true) {
        if (x < 5) goto start;
        x++
    }

你可以像这样给语句编号:

0 start:
1     while (x < 3) {
2         if (x < 5) goto start;
3         x++
      }

要消除所有 goto,您可以通过使用 while 循环、保存程序计数器的显式变量和一堆 if 语句来模拟通过此函数的控制流程。例如,你可以这样翻译上面的代码:

int PC = 0;
while (PC <= 3) {
    if (PC == 0) {
         PC = 1;             // Label has no effect
    } else if (PC == 1) {
         if (x < 3) PC = 4;  // Skip loop, which ends this function.
         else PC = 2;        // Enter loop.
    } else if (PC == 2) {
         if (x < 5) PC = 0;  // Simulate goto
         else PC = 3;        // Simulate if-statement fall-through
    } else if (PC == 3) {
         x++;
         PC = 1;             // Simulate jump back up to the top of the loop.
    }
}

这是一种非常非常糟糕的翻译方式,但它表明理论上总是可以做到这一点。实际上实现这将非常混乱 - 你可能会为函数的基本块编号,然后生成将基本块放入循环的代码,跟踪当前正在执行的基本块,然后模拟运行基本块的效果和从那个基本块到适当的下一个基本块的过渡。

希望这会有所帮助!

【讨论】:

  • 顺便说一句:这是 Jacopini 方法。 (谷歌“使用 goto Knuth 进行结构化编程”)
  • @wildplasser- 我不知道它有名字!感谢您的参考!
  • 我刚刚阅读了 Knuth 的文章。顺便说一句,它可能有很好的观察结果。 Knuth 最近(~2004 年?)发表了另一个 goto 驱动的状态机;实际上生成的代码带有计算和有意义的标签(几千个......),但我似乎再也找不到它了。它既美丽又有趣。
  • This paper 详细讨论转换。他们引用了 Jacopini,但他们声称他们的工作更完整。
  • @Asiri:是否可以从任何未锁定在付费墙后面的来源获得?这使得您的链接对像这样的一般问答网站的用处不大......
【解决方案2】:

我认为您想阅读 Erosa 和 Hendren 于 1994 年撰写的 Taming Control Flow。(Google scholar 上的早期链接)。

顺便说一句,循环中断也很容易消除。有一个简单的机械过程,涉及创建布尔状态变量和重组嵌套条件以创建直线控制流。它不会产生漂亮的代码:)

如果您的目标语言具有尾调用优化(理想情况下是内联),您可以通过将循环转换为尾递归函数来机械地删除 break 和 continue。 (如果索引变量被循环体修改了,这点你需要加倍努力,我只展示最简单的情况。)下面是一个简单循环的转换:

for (Type Index = Start;        function loop(Index: Type):    
     Condition(Index);              if (Condition)
     Index = Advance(Index)){           return                      // break
   Body                             Body
}                                   return loop(Advance(Index))     // continue
                                loop(Start)

标记为“继续”和“中断”的return 语句正是continuebreak 的转换。实际上,该过程的第一步可能是将循环重写为原始语言中的等效形式:

{
    Type Index = Start;
    while (true) {
        if (!Condition(Index))
            break;
        Body;
        continue;
    }
}

【讨论】:

  • 驯服控制流程文件是否可以从任何未锁定在付费墙后面的来源获得?
  • 那篇论文正是我要找的。它以清晰的语言解释了如何将带有 GOTO 的例程转换为没有 GOTO 的例程,并解释了如何完全进行该转换,而不是 templatetypedef 的答案中给出的蛮力描述,它确实更多,以牺牲效率和可读性为代价。
【解决方案3】:

我使用 Polyhedron 的 spag 和 large 的 77to90 中的一个/两个来开始重构 fortran 的过程,然后将其转换为 matlab 源代码。但是,这些工具总是在程序中留下 1/4 到 1/2 的 goto。

我编写了一个 goto remover,它完成了与您所描述的类似的事情:它采用 fortran 代码并重构程序中所有剩余的 goto,并用条件和 do/cycle/exit 替换它们,然后可以将其转换为其他matlab 之类的语言。您可以在此处阅读有关我使用的流程的更多信息:

http://engineering.dartmouth.edu/~d30574x/consulting/consulting_gotorefactor.html

这个程序可以适应其他语言,但我还没有做到这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-28
    • 1970-01-01
    • 2012-12-16
    • 2020-10-19
    • 1970-01-01
    • 2021-01-15
    • 1970-01-01
    相关资源
    最近更新 更多