Parsing 如何使用ANTLR正确解析泛型类型?

Parsing 如何使用ANTLR正确解析泛型类型?,parsing,antlr,antlr3,Parsing,Antlr,Antlr3,我使用的是ANTLR v3,我希望正确解析模板表达式,如: List<List<int>> 列表 问题在于,两个大于关闭标志与右移操作员冲突。我已经看到了一些解决方案,但大多数看起来都像是权宜之计,例如根本没有正确的转换,但只是更大的。首先,我希望正确地进行此解析。此问题的标准解决方案是根本没有'>'标记。相反,您只有'>'标记,并且在解析器中匹配两个连续的'>'。然后,为了确保您没有匹配其他内容,您需要在之后执行检查,以验证它是否真的是'>'。请参阅@280Z28的

我使用的是ANTLR v3,我希望正确解析模板表达式,如:

List<List<int>>
列表

问题在于,两个大于关闭标志与右移操作员冲突。我已经看到了一些解决方案,但大多数看起来都像是权宜之计,例如根本没有正确的转换,但只是更大的。首先,我希望正确地进行此解析。

此问题的标准解决方案是根本没有
'>'
标记。相反,您只有
'>'
标记,并且在解析器中匹配两个连续的
'>'
。然后,为了确保您没有匹配其他内容,您需要在之后执行检查,以验证它是否真的是
'>'
。请参阅@280Z28的答案,了解如何使用该方法

下面的解决方案是另一种方法。这种方法允许您在lexer中实际拥有一个
'>'
令牌。基本上,它的工作原理是单个
'>>'
令牌作为两个虚拟令牌发出。这两个令牌可以单独匹配,也可以一起匹配(就像标准解决方案一样)。主要区别在于,这两个标记之间不可能有任何类似的内容,因此不需要您验证是否实际匹配了
'>'

首先,您必须调整lexer以支持发出多个令牌。请参见如何完成此操作

接下来是lexer规则。我们有两条lexer规则。大于标记的lexer规则与您通常所做的一样:

OP_GREATER_THAN : '>' ;
右移是魔术发生的地方。我们不发送右移标记,而是发送多个标记:

OP_GREATER_THAN_GREATER_THAN : t='>>' { emitGreaterThanGreaterThan(t); } ;
emitgreaterthan()
方法发出以下信息:

private void emitGreaterThanGreaterThan(Token token) {
    // Split the greater than token into two tokens so they can be matched separately.

    CommonToken first = new CommonToken(token);
    first.setType(OP_GREATER_THAN_GREATER_THAN_FIRST);
    emit(first);

    CommonToken second = new CommonToken(token);
    second.setType(OP_GREATER_THAN_GREATER_THAN_SECOND);
    emit(second);
}
此方法可以添加到lexer中的
@lexer::members
部分。此方法的作用是获取
'>'
标记,复制它两次并更改类型。它将单次右移标记转换为
OP\u大于\u大于\u第一次
OP\u大于\u大于\u第二次
。必须声明这些令牌,因此必须添加以下lexer规则:

fragment OP_GREATER_THAN_GREATER_THAN_FIRST : ;
fragment OP_GREATER_THAN_GREATER_THAN_SECOND : ;
op_GREATER_THAN_GREATER_THAN
    :
        OP_GREATER_THAN_GREATER_THAN_FIRST
        OP_GREATER_THAN_GREATER_THAN_SECOND
    ;

op_GREATER_THAN_ANY
    : OP_GREATER_THAN
    | OP_GREATER_THAN_GREATER_THAN_FIRST
    | OP_GREATER_THAN_GREATER_THAN_SECOND
    ;
这些lexer规则不匹配任何东西,但是在这里只是为了让我们可以引用它们

为了使使用此构造更容易一些,可以添加一些解析规则:

fragment OP_GREATER_THAN_GREATER_THAN_FIRST : ;
fragment OP_GREATER_THAN_GREATER_THAN_SECOND : ;
op_GREATER_THAN_GREATER_THAN
    :
        OP_GREATER_THAN_GREATER_THAN_FIRST
        OP_GREATER_THAN_GREATER_THAN_SECOND
    ;

op_GREATER_THAN_ANY
    : OP_GREATER_THAN
    | OP_GREATER_THAN_GREATER_THAN_FIRST
    | OP_GREATER_THAN_GREATER_THAN_SECOND
    ;
解析规则
op_beater_THAN_beater_THAN
匹配第一个标记,后跟第二个标记。这将成为右移标记。
op\u大于任何
规则匹配任何大于标记。这用于解析泛型类型

泛型类型规则如下所示:

genericTypeArguments
    :
        OP_LESS_THAN
        genericTypeArgument
        (
            OP_COMMA
            genericTypeArgument
        )*
        op_GREATER_THAN_ANY
    ;

这就是魔法发生的地方。因为我们已经将
'>'
标记拆分为多个标记,所以我们可以匹配一个大于标记、右移位标记的第一部分和右移位标记的第二部分。

在某些情况下,输入
>
是两个标记(两个直角括号闭合泛型类型参数),在其他情况下,它是一个单一的运营商。要正确处理这些情况,请始终将单个直角大括号视为lexer中的单个标记,并使用解析器规则区分这些情况。其中一个就是这样做的。您可以使用代码验证shift运算符在角括号之间不包含任何无关字符,而不会影响语法本身的可移植性

词法分析规则
GT:'>';
LT:'
|   '>' '>'
;
代码(ANTLR 3) 我这里没有具体的例子,因为实现依赖于语法使用的中间格式(即output=AST或其他格式)。我强烈建议所有的新开发都使用ANTLR4来完成,原因太多了

代码(ANTLR 4) 注意:在ANTLR4中,这可以在侦听器或访问者中执行。我在这里随意使用了一个侦听器方法

@Override
public void enterShiftOp(ShiftOpContext ctx) {
  Token token = ((TerminalNode)ctx.getChild(0)).getSymbol();
  int firstIndex = token.getStartIndex();
  for (int i = 1; i < ctx.getChildCount(); i++) {
    Token sibling = ((TerminalNode)ctx.getChild(i)).getSymbol();
    if (sibling.getStartIndex() != firstIndex + i) {
      // report error: spaces/comments cannot appear between chars of a shift operator
    }
  }
}
@覆盖
公共无效enterShiftOp(ShiftOpContext ctx){
令牌令牌=((TerminalNode)ctx.getChild(0)).getSymbol();
int firstIndex=token.getStartIndex();
对于(int i=1;i
我做了一些研究来解决这个问题,你的研究是我遇到的解决方案之一。我之所以喜欢我提供的解决方案,是因为这样您就不必执行修复。它可能有点复杂,但我认为它比事后检查无关字符要复杂得多。如果包含验证代码,我无法想象这比您的方法更复杂。不管怎样,代码是为任何想要另一种方法的人准备的:)。