使用backtrack选项时,编译器的性能会急剧下降

时间:2014-12-02 09:50:01

标签: antlr3

我在Windows7上使用C#版本的ANTLR3为C语言实现了一个Transpiler。

要解析if / else语句,我使用以下规则:

ifStatement
: 'if' '(' expression ')' b1=block
   (
       'else' b2=block      -> ^('if' expression $b1 $b2 'else')
   |   'else' ifStatement   -> ^('if' expression $b1 ^(ELSIF ifStatement))
   |                        -> ^('if' expression $b1)
   )
;

要将此规则生成的树从我自己的语言翻译成C#,请使用以下语法片段:

ifStatement
options { backtrack = true; }
: 
     ^(n='if' expression b1=block)
     -> if(
          node={$n},
          cond={$expression.st},
          block1={$b1.st},
          block2={null},
          isElsif={($n.Parent.Text == "ELSIF") ? "true" : null},
          node2={null}
        )
|
     ^(n='if' expression b1=block b2=block n2='else')
     -> if(
          node={$n},
          cond={$expression.st},
          block1={$b1.st},
          block2={$b2.st},
          isElsif={($n.Parent.Text == "ELSIF") ? "true" : null},
          node2={$n2}
        )
|
     ^(n='if' expression b1=block b2=ifStatement)
     -> elsif(
          node={$n},
          cond={$expression.st},
          block1={$b1.st},
          block2={$b2.st},
          isElsif={($n.Parent.Text == "ELSIF") ? "true" : null}
        )
|
     ^(ELSIF i=ifStatement) -> { $i.st }
;

这在大多数情况下都可以正常工作,例如它可以毫无问题地翻译以下代码:

if (x == "1") {
}
else if (x == "2") {
}
else if (x == "3") {
}

但是当我超过20岁时,其他如果"从句,编译器需要几分钟才能完成它的工作。编译所需的时间不会线性增加,即编译器会立即返回17或18"否则,如果"条款。

更新

我已经解决了这个问题。我已经取代了

     ^(n='if' expression b1=block b2=ifStatement)
     -> elsif(
          node={$n},
          cond={$expression.st},
          block1={$b1.st},
          block2={$b2.st},
          isElsif={($n.Parent.Text == "ELSIF") ? "true" : null}
        )
|
     ^(ELSIF i=ifStatement) -> { $i.st }

     ^(n='if' expression b1=block ^(ELSIF b2=ifStatement))
     -> elsif(
          node={$n},
          cond={$expression.st},
          block1={$b1.st},
          block2={$b2.st},
          isElsif={($n.Parent.Text == "ELSIF") ? "true" : null}
        )

这就是我将最后两个选择合并为一个。

现在,Transpiler速度非常快,仍能保持正确的结果。

我真的很想知道为什么这种变化会产生如此大的影响。

3 个答案:

答案 0 :(得分:2)

欢迎回溯。

如果强制解析器在(例如)你的语法中选择(例如),并且它必须回溯,并且选择嵌套 [作为你的if-then-否则确实如此,那么嵌套的N个构造集可能需要3 ^ N个单位的工作来解析。 3 ^ 20是3 ^ 17的27倍。

课程:回溯有时很有用,但通常你应该避免它。

对于你的语法,为什么不像其他语句一样对待if结构?然后它不会显示为一个特例,你可以完全避免回溯。您甚至可以获得标准"否则会附加到最近的标准"在大多数编程语言中都是标准的规则。

答案 1 :(得分:1)

尝试重写语法而不回溯整个规则。如果你不能,那么至少要加memoize=true来加快速度:

options { backtrack = true; memoize=true; }

答案 2 :(得分:1)

根本不确定为什么你需要回溯这条规则。 block也可以是ifStatement吗?如果是这样,为else ifStatement部分添加语义谓词,以检查下一个标记是否实际为IF。否则,您始终可以使用block替代方案。

另外,如果可以,请避免树重写。我发现创建和重写AST可以使解析时间加倍。