我正在使用antlr4的C 版本为音乐产品开发DSL。我曾经(30年前!(手工做这种事情,所以拥有像Antlr这样的东西是一种荣幸,尤其是现在我不必在实际的语法定义中插入代码。
我想在函数调用中对实际VS正式ARG进行类型检查。在下面的语法段中,"实际参数"可以返回表达式的类型。但是,"实际参数列表"需要返回这些类型的数组(例如(,以便functionCall的代码可以与正式参数列表进行比较。
如果我是在手写,访问或Visitchildren的电话会在上下文之后进行额外的参数,以便我可以在适当的位置创建一个新数组,然后让子节点填写详细信息。
我想,我不仅可以在" vistactualparameterlist"中打电话给Visitchildren,我可以在那里创建阵列并手动打电话给每个孩子,而不仅仅是一个简单的Visitchildren,但这感觉就像是一个黑客,这对中的微小变化非常敏感语法。
有更好的方法吗?
functionCall: Identifier LeftParen actualParameterList? RightParen
;
actualParameterList:
actualParameter anotherActualParameter
;
actualParameter:
expression
;
anotherActualParameter:
Comma actualParameter anotherActualParameter
|
;
您处在正确的路径上。我建议这样的东西:
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。您可以使用相同的模式来构建所需的一切。