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

时间:2014-06-16 04:49:00

标签: parsing antlr antlr3

我正在使用ANTLR v3,我想正确解析模板表达式,如:

List<List<int>>

问题在于两个大于标志的关闭与右移操作员发生冲突。我已经看到了一些解决方案,但大多数解决方案看起来像解决方法,例如没有正确的转变,但只是大于。我想首先正确解析这个问题。

2 个答案:

答案 0 :(得分:2)

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

Lexer规则

GT : '>';
LT : '<';

解析器规则

typeArguments
    :   '<' typeArgument (',' typeArgument)* '>'
    ;

shiftOp
    :   '<' '<'
    |   '>' '>' '>'
    |   '>' '>'
    ;

代码(ANTLR 3)

我这里没有具体的例子,因为实现将取决于语法使用的中间格式(即output = AST或其他一些格式)。我强烈建议使用ANTLR 4完成所有新开发,原因很多,无法列出。

代码(ANTLR 4)

注意:在ANTLR 4中,这可以在听众或访客中执行。我在这里任意使用了一个监听器方法。

@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
    }
  }
}

答案 1 :(得分:0)

此问题的标准解决方案是根本没有'>>'令牌。相反,您只有'>'标记,并且在解析器中您匹配两个连续的'>'标记。然后,为了确保您没有与其他内容匹配,您之后会执行检查以确认它确实是'>>'。请参阅@ 280Z28的答案,了解如何以这种方式进行解释。

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

首先,您必须调整词法分析器以支持发出多个令牌。有关如何完成此操作,请参阅How can I emit more than a single token per lexer rule?

接下来是词法分析器规则。我们有两个词法规则。大于令牌的词法分析器规则就像您通常这样做:

OP_GREATER_THAN : '>' ;

右移是神奇发生的地方。我们不会发出右移令牌,而是发出多个令牌:

OP_GREATER_THAN_GREATER_THAN : t='>>' { emitGreaterThanGreaterThan(t); } ;

emitGreaterThanGreaterThan()方法会发出以下信息:

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::members部分。这种方法的作用是它采用'>>'标记,将其复制两次并更改类型。它将单个右移令牌转换为OP_GREATER_THAN_GREATER_THAN_FIRSTOP_GREATER_THAN_GREATER_THAN_SECOND。必须声明这些标记,因此必须添加以下词法分析器规则:

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_GREATER_THAN_GREATER_THAN匹配第一个令牌,后跟第二个令牌。这成为右移令牌。 op_GREATER_THAN_ANY规则匹配任何大于令牌。这用于解析泛型类型。

泛型类型规则如下所示:

genericTypeArguments
    :
        OP_LESS_THAN
        genericTypeArgument
        (
            OP_COMMA
            genericTypeArgument
        )*
        op_GREATER_THAN_ANY
    ;

这就是魔术发生的地方。因为我们已将'>>'令牌拆分为多个令牌,所以我们可以匹配裸大于令牌,右移令牌的第一部分和右移令牌的第二部分。