我在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速度非常快,仍能保持正确的结果。
我真的很想知道为什么这种变化会产生如此大的影响。
答案 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可以使解析时间加倍。