我使用C ++版本的ANTLR4为音乐产品开发DSL。我曾经(30年前!)手工做这种事情,所以很高兴能有类似ANTLR的东西,特别是现在我不必在实际的语法定义中插入代码
我想在函数调用中对实际与正式args进行类型检查。在下面的语法部分中,' actualParameter'可以返回表达式的类型。但是,' actualParameterList'需要返回这些类型的数组(例如),以便functionCall的代码可以与形式参数列表进行比较。
如果我手写这个,那么访问或visitChildren的调用将在上下文之后获取一个额外的参数,这样我就可以在适当的位置创建一个新数组,然后让子节点填入详细信息。
我想这不是仅仅在访问实际参数列表中调用visitChildren。我可以在那里创建数组并手动调用每个孩子,而不仅仅是一个简单的visitChildren,但这感觉就像一个hack,并且它对语法中的微小变化变得非常敏感。
有更好的方法吗?
functionCall: Identifier LeftParen actualParameterList? RightParen
;
actualParameterList:
actualParameter anotherActualParameter
;
actualParameter:
expression
;
anotherActualParameter:
Comma actualParameter anotherActualParameter
|
;
答案 0 :(得分:11)
你走在正确的道路上。我会建议像:
functionCall: Identifier LPAREN actualParameterList RPAREN
;
actualParameterList:
actualParameter (',' actualParameter)*
;
actualParameter:
expression
;
LPAREN : '(';
RPAREN : ')';
使用此功能,在actualParameterList
的访问者中,您可以检查每个孩子是否属于actualParameterContext
类型,如果是,请明确调用该孩子的访问 会让你进入你的表达式评估代码(可能是在actualParameter
的访问者中处理的)。正如你所说,这可以减少一般性地探望儿童的需要。当你可以检查这样的类型时,它非常精确。
这是我自己的代码中的这种模式的一个例子(在C#中,但你肯定会看到模式在运行):
for (int c = 0; c < context.ChildCount; c++)
{
if (context.GetChild(c) is SystemParser.ServerContext) // make sure correct type
{
string serverinfo = Visit(context.GetChild(c)); // visit the specific child and save return value, string in this case
sb.Append(serverinfo); // use result to fill array or do whatever
}
}
现在您可以看到模式,返回到您的代码。语法:
actualParameter (',' actualParameter)*
表示参数列表中有一个actualParameter
后跟零{或}个*
运算符。为了清晰起见,我只是把逗号放在那里。
正如您所说,访问者是完美的模式,因为您可以显式访问您需要的任何节点。它不会给你你一个数组,但你可以填充一个数组或任何其他必要的结构,看看你在我的代码的剪辑中看到的访问孩子的结果。我的访问者返回字符串,我只是附加到StringBuilder。您可以使用相同的模式构建您需要的任何内容。