在使用访问者模式时,如何在树中传递结构(即继承的属性)?

时间:2017-06-07 12:33:27

标签: antlr4

我使用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
|
;   

1 个答案:

答案 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。您可以使用相同的模式构建您需要的任何内容。