2011-10-06 43 views
5

我有一大堆遗留代码以旧的自我构想的脚本语言编译/转换为javascript。重写修改后的goto语义的算法

该语言有条件跳转,跳转到标签。与常见的goto语句不同之处在于,没有后向跳转是可能的。没有嵌套的if语句或该语言中的循环。

由于goto在javascript中不存在,我正在寻找一种将goto mylabelmylabel:转换为语义等效结构的算法。

我想到了使用ifs,但发现它并不重要,因为任意嵌套goto标签。

实施例:

lbl_b=false; 
lbl_c=false; 

lbl_a = cond1; 
if (!cond1) { 
    do something1; 
    lbl_b = cond2; 
    if (!lbl_b) { 
    do something2; 
    } 
} 
if (!lbl_b) { 
    do something3; 
    lbl_c = cond3; 
    if (!lbl_c) { 
    do something4; 
    } 
    do something5; 
} 

然而,我无法从该导出通用算法:

if cond1 goto a 
do something1 
if cond2 goto b 
do something2 
a: 
do something3 
if cond3 goto c 
do something4 
c: 
do something5 
b: 

能作为被重写。

+0

一般goto删除解决方案是在“Taming Control Flow”论文中引用的:http://stackoverflow.com/questions/14061856/automated-goto-removal-algorithm –

回答

2

这通常被称为转到去除的一切,所有的标签位置等,我们只有一次,一个学生工作的任务是为C实现它。一般来说,你必须使用循环(可惜我们没有把这个工作放到网上)。但是,由于您有限制,只能向前跳跃,所以相对容易:

解析所有行并收集所有标签。为每个标签创建一个标志“skip_to_label”。初始化所有标志为false。当您遇到标签X的条件跳转时,您现在将每行添加一行,直到标签行显示“if not skip_to_label”并将该标志设置为true。

这应该已经够用了,但是当然不是很理想。

如何优化它:与其准备if,只是为每一行维护一组标志,而不是将某些设置为false,只需在行中添加corrosponding标志即可。

现在您可以对包含所有行的组进行if操作,该集合不会更改,条件是集合的布尔标志。

实例与给定的代码:

set     your code 
empty     if cond1 goto a 
skip_to_a,    do something1 
skip_to_a,    if cond2 goto b 
skip_to_a, skip_to_b do something2 
skip_to_a, skip_to_b a: 
skip_to_b    do something3 
skip_to_b, skip_to_c if cond3 goto c 
skip_to_b, skip_to_c do something4 
skip_to_b, skip_to_c c: 
skip_to_b    do something5 
skip_to_b    b: 

现在你在每行前面写无论是如果(一个或多个),或者你从顶部开始并作出是否块,只要设置保持不变。

所以,当你开始你得到你先在空的,其条件转到所以不是你设置你的标志

if cond1 goto skip_to_a=true; 

现在设定的变化,你的如果设定的介绍你的块:

if (!skip_to_a) BEGIN 
    do something1 
    if cond2 skip_to_b=true; 
    END 

在组下一个变化,所以新if块:

if (!skip_to_a and !skip_to_b) BEGIN 
    do something2 
    END 

等(我猜你GE现在这个想法)。

编辑:正如人们可以在示例中很好地看到集合一样,通常不可能用嵌套的ifs对其进行建模,例如, skip_to_a的行和skip_to_b的行重叠,但都不包含其他完整行。

+2

而不是布尔标志,你可以使用单个整数变量,指示您正在瞄准哪个标签。那么第N个块前面的条件就是'if(skip_to <= N)'并且'goto Mth_label'被替换为'skip_to = M'。 – han

+0

@han:我猜这是行不通的。在上面的例子中,你可以很容易地用1,2,3替换a,b,c。你会发现,只有当你跳过一个较低的标签时,它才会正常工作。不是这种情况。你可能会争辩说,两个数字是足够的(最低和最高的标签) - 在这个例子中确实可行。但我想我们可以构建一个不起作用的反例(我在标签间插入“空洞”)。 – flolo

+0

我不明白你的观点。标签应按照它们在代码中出现的顺序编号,即a,c,b被1,2,3替换。然后跳转到较高编号的标签意味着跳过当前指令和目标标签之间的所有较低编号的标签。 “ – han

1

你可以做这样的事情在一个while循环跟踪进入状态,但它不会看起来太漂亮:

var goto = null ; 

do { 
    if(goto == null && cond1) goto = 'a' ; 
    if(goto == null) do_something(1) ; 
    if(goto == null && cond2) goto = 'b' ; 
    if(goto == null) do_something(2) ; 
    if(goto == null || goto == 'a') goto = null; 
    if(goto == null) do_something(3) ; 
    if(goto == null && cond3) goto = 'c' ; 
    if(goto == null) do_something(4) ; 
    if(goto == null || goto == 'c') goto = null ; 
    if(goto == null) do_something(5) ; 
    if(goto == null || goto == 'b') goto = null ; 
} while(goto != null) 
1

编译成另一种语言通常比必要的努力。更简单的方法是,不编译为其他语言,而是用JavaScript解释代码。通过这种方式,很容易就可以用任何你想要的语义来生成你的goto语句。

但是,如果你这样做,你需要将所有的解析逻辑转移到你的javascript代码中,这可能很难做到。另一种方法是编译一些比较容易解释的格式,即字节码,这样就可以预先计算你从解析器需要

1

一种替代解决方案是将每个标签制作成一种方法,该方法包含从该标签开始到下一个标签开头的代码,然后调用为下一个标签生成的函数。

专业人士可以用一个简单的方法调用代替goto。缺点是,对于长脚本或循环,最终可能会产生相当大的调用堆栈。

使用这种方法,一个简单的算法是:

goto_label_count := 0 // Find out how many methods we need to generate 
For each line: 
    if line is goto 
     goto_label_count := goto_label_count + 1 

Write function head 
Write "goto0();" 

goto_count := 0 // Generate code 
For each line: 
    if line is goto 
     if goto_count > 0 
      write "goto"+goto_count+"();" // produce call to last goto found 
     write function header for corresponding goto //("goto"+goto_count+"()") 
     goto_count := goto_count + 1 
    else 
     translate normally 
Generate end of code 

这最终可能会导致其他问题。例如,变量的作用域是什么?但是,至少这是一种替代方法,我希望你的思想能够沿着更多轨道开始。 ;)

+0

”从该标签开始到该标签开头的代码“?寮步? –

+0

感谢您的注意。这意味着“从该标签开始到下面标签开头的代码” 该帖已更正。 – Agentlien