我从不使用除 1 之外的全局设置 LOOKAHEAD。相反,我会在需要的地方使用局部前瞻。在您的情况下,我会执行以下操作
double Func() :
{}
{
<STRING>
"("
(
( LOOKAHEAD( <STRING> "," )
<STRING>
| Expression()
)
","
)*
")"
}
在最后一个参数之后需要一个逗号是相当奇怪的。如果你不想这样,你可以这样做
double Func() :
{}
{
<STRING>
"("
(
( LOOKAHEAD( <STRING> ("," | ")") ) <STRING> | Expression() )
( ","
( LOOKAHEAD( <STRING> ("," | ")") ) <STRING> | Expression() )
)*
)?
")"
}
然而,在上面的两个 sn-ps 中,这些前瞻规范违反了 FAQ 4.8 中的建议,即由句法前瞻规范扫描的标记都应该被选择所消耗。如果您曾经在前瞻规范中使用 func() 本身,这可能是一个问题。对于第一种语法,这很容易处理:像这样分配逗号
double Func() :
{}
{
<STRING>
"("
(
( LOOKAHEAD(<STRING> "," )
<STRING> ","
| Expression() ","
)
)*
")"
}
对于第二种语法(最后一个参数后没有逗号的语法),您可以像这样使用递归:
double Func() :
{}
{
<STRING> "(" ( ")" | Args() )
}
void Args() :
{}
LOOKAHEAD(<STRING> ("," | ")" )
<STRING>
("," Args() | ")" )
|
Expression()
("," Args() | ")" )
}
递归版本的另一个好处是它比迭代版本的重复更少。