了解Antlr4中的上下文数据结构

时间:2014-06-12 13:37:21

标签: java parsing antlr4 visitor

我正试图在Antlr4的帮助下用Java编写代码转换器,并且到目前为止在语法部分取得了很大的成功。然而,现在我正在把我的脑袋撞到围绕解析树数据结构的墙上,我需要在解析输入后进行处理。

我正在尝试使用访问者模板来查看我的解析树。我将举例说明我的困惑。

我的语法:

grammar pqlc;

// Lexer

//Schlüsselwörter
EXISTS: 'exists';
REDUCE: 'reduce';
QUERY: 'query';
INT: 'int';
DOUBLE: 'double';
CONST: 'const';
STDVECTOR: 'std::vector';
STDMAP: 'std::map';
STDSET: 'std::set';
C_EXPR: 'c_expr';

INTEGER_LITERAL  : (DIGIT)+ ;
fragment DIGIT: '0'..'9';
DOUBLE_LITERAL : DIGIT '.' DIGIT+;

LPAREN          : '(';
RPAREN          : ')';
LBRACK          : '[';
RBRACK          : ']';
DOT             : '.';
EQUAL           : '==';
LE              : '<=';
GE              : '>=';
GT              : '>';
LT              : '<';
ADD             : '+';
MUL             : '*';
AND             : '&&';
COLON           : ':';

IDENTIFIER    :   JavaLetter JavaLetterOrDigit*;
fragment JavaLetter    :   [a-zA-Z$_]; // these are the "java letters" below 0xFF
fragment JavaLetterOrDigit    :   [a-zA-Z0-9$_]; // these are the "java letters or digits" below 0xFF
WS  
    :  [ \t\r\n\u000C]+ -> skip  
    ;
COMMENT
    :   '/*' .*? '*/' -> skip
    ;

LINE_COMMENT
    :   '//' ~[\r\n]* -> skip
    ;


// Parser

//start_rule: query;

query :
      quant_expr
      | qexpr+
      | IDENTIFIER // order IDENTIFIER and qexpr+?
      | numeral
      | c_expr //TODO

      ;

c_type : INT | DOUBLE | CONST;
bin_op: AND | ADD | MUL | EQUAL | LT | GT | LE| GE;


qexpr:
         LPAREN query RPAREN bin_op_query? 
         // query bin_op query
         | IDENTIFIER  bin_op_query? // copied from query to resolve left recursion problem
         | numeral bin_op_query?  // ^
         | quant_expr bin_op_query? // ^
           |c_expr bin_op_query?
           // query.find(query)
         | IDENTIFIER  find_query? // copied from query to resolve left recursion problem
         | numeral find_query?  // ^
         | quant_expr find_query?
           |c_expr find_query?
           // query[query]
          | IDENTIFIER  array_query? // copied from query to resolve left recursion problem
         | numeral array_query?  // ^
         | quant_expr array_query?
           |c_expr array_query?

     // | qexpr bin_op_query // bad, resolved by quexpr+ in query 
     ;

bin_op_query: bin_op query bin_op_query?; // resolve left recursion of query bin_op query

find_query: '.''find' LPAREN query RPAREN;
array_query: LBRACK query RBRACK;

quant_expr:
    quant id ':' query
          | QUERY LPAREN match RPAREN ':' query
          | REDUCE LPAREN IDENTIFIER RPAREN id ':' query
    ;

match:
         STDVECTOR LBRACK id RBRACK EQUAL cm
     | STDMAP '.''find' LPAREN cm RPAREN EQUAL cm
     | STDSET '.''find' LPAREN cm RPAREN
     ;

cm:
    IDENTIFIER
  | numeral
   | c_expr //TODO
  ;

quant :
          EXISTS;

id :
     c_type IDENTIFIER
     | IDENTIFIER // Nach Seite 2 aber nicht der Übersicht. Laut übersicht id -> aber dann wäre Regel 1 ohne +
   ;

numeral :
            INTEGER_LITERAL
        | DOUBLE_LITERAL
        ;
c_expr:
          C_EXPR
      ;

现在让我们解析以下字符串:

double x: x >= c_expr

视觉上我会得到这棵树: tree

假设我的访问者在遇到分支Qexpr(x bin_op_query)时处于visitQexpr(@NotNull pqlcParser.QexprContext ctx)例程中。

我的问题是,如何判断左边的孩子(树中的“x”)是终端节点,还是更具体地说是“IDENTIFIER”?终端节点没有访问规则,因为它们不是规则。 ctx.getChild(0)没有RuleIndex。我想我可以用它来检查我是否在终端,但是如果我在IDENTIFIER或其他类型的终端令牌,那仍然不会告诉我。我需要以某种方式区分它。

我有更多的问题,但是在我写这篇解释的时候我忘了它们:&lt; 提前谢谢。

1 个答案:

答案 0 :(得分:3)

您可以为令牌添加标签并访问它们/检查它们是否存在于周围环境中:

id :
     c_type labelA = IDENTIFIER
     | labelB = IDENTIFIER 
   ;

你也可以这样做来创建不同的访问:

id :
     c_type IDENTIFIER    #idType1 //choose more appropriate names!
     | IDENTIFIER         #idType2
   ;

这将为这两个选项创建不同的访问者,我想(即尚未验证)id的访问者将不会被调用。

我更喜欢以下方法:

id :
        typeDef
     |  otherId
     ;
typeDef: c_type IDENTIFIER;
otherId : IDENTIFIER ;

这是一个更加严格的系统。但是你可以非常专门地访问节点。我使用的一些经验法则:

  1. 仅当所有备选方案都是解析器规则时才使用|
  2. 将每个令牌包装在解析器规则中(如otherId)给予他们&#34;更多含义&#34;。
  3. 如果令牌不是很重要(例如;),那么可以混合解析器规则和令牌,因此在解析树中不需要。