在lexer/parser中使用Goto
本文关键字:Goto parser lexer | 更新日期: 2023-09-27 18:00:50
我有一个lexer/parser对(几年前我从别人那里抄袭的(。我将添加几个功能,并认为我将首先标准化while(true(的使用,其中包含多个if/else-if/else与使用goto跳回切换之前的切换。
(在火焰开始之前,我通常不会使用后藤作为它的邪恶等等(
while(true(和嵌套开关的问题在于,break只从开关中断开,不能超出while。
我在这里做了一些搜索,并看到了使用开关内部返回的建议。虽然这在某些情况下会起作用,但在其他情况下,在返回之前会进行一些处理。在多个地方重复这个代码并没有什么吸引力。
我也可以引入一个布尔标志,并在while语句中使用它来决定是否突破while,但这也没有吸引力,因为它会给代码添加噪音。
解析器中使用if/else-if/else而不是内部开关的当前方式是有效的,但如果可能的话,我确实更喜欢开关。
一般来说,lexer代码似乎可以通过删除while(true(并在开关启动前放置一个标签,并使用goto来继续循环来解决这个问题。这让break意味着停止循环,老实说,这似乎是最干净的方式,但确实涉及到了可怕的后藤。
回到过去(真的(,我也能看到第三条路。在while(true(之后使用一个标签,并让开关代码使用goto在循环结束时到达它。中断意味着退出开关,但继续循环。
小组对此有何看法?后藤是不是太可恶了?或者,当只有一个标签可以跳转并减少缩进并生成清晰的代码时,这可以吗?解析器/lexer应该获得使用gotos的特殊许可吗?
如果有帮助的话,我可以提供一些示例代码。
以有纪律的方式使用GOTO是可以的。自20世纪70年代以来,不允许突破任意嵌套的块结构的语言导致了这个问题的反复提出,当时人们将"语言应该具有什么控制流结构"的问题打得落花流水。(注意:这个抱怨对于lexers/parser来说并不是特别的(。
您不希望方案带有布尔值;它只是给循环检查增加了额外的开销,并扰乱了代码。
我想你有这个问题:
<if/while/loop head> {
<if/while/loop head> {
...
if <cond> <want to break out all blocks>
...
}
}
用一种好的语言治疗的正确方法是:
blocks_label:
<if/while/loop head> {
<if/while/loop head> {
...
if <cond> exit blocks_label;
...
}
}
如果您的语言中存在exit构造,则退出由命名标签标记的块。(没有任何借口现代语言没有这个,但是,我没有设计它们(。
作为一个穷人的替代品写作是完全令人满意的:
<if/while/loop head> {
<if/while/loop head> {
...
if <cond> goto exit_these_blocks;
...
}
}
exit_these_blocks: // my language doesn't have decent block exits
有时你会发现一种提供的语言
break <exp>
其中exp通常是一个常数整数,意思是"突破expr嵌套块"。这是一个非常愚蠢的想法,因为一些糟糕的维护人员稍后可能会在堆栈中的某个地方插入另一个块,而现在代码会做一些疯狂的事情。(事实上,大约20年前,电信公司交换机的这个错误摧毁了整个东海岸的电话系统(。如果你在你的语言中看到了这个结构,那就用穷人的替代品吧。
在解析器中,GOTO的使用是完全合理的。当你进入基本级别时,循环和条件等都被实现为gotos,因为这就是处理器可以做的——"从这里获取下一条要执行的指令"。
gotos的唯一问题,以及它们经常被妖魔化的原因,是它们可能是非结构化代码的一个标志,来自非结构化思维。在现代高级语言中,不需要gotos,因为所有的工具都可以很好地构建代码,而结构良好的代码至少意味着一些结构化的思维。
因此,如果需要,可以使用goto。不要仅仅因为你不想把事情想清楚就使用它们。