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

use*_*596 5 java parsing visitor antlr4

我试图在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
      ;
Run Code Online (Sandbox Code Playgroud)

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

double x: x >= c_expr
Run Code Online (Sandbox Code Playgroud)

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

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

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

我有更多的问题,但是在我写这个解释的时候我忘了他们:<提前谢谢.

Onu*_*nur 3

您可以向令牌添加标签并访问它们/检查它们是否存在于周围的上下文中:

id :
     c_type labelA = IDENTIFIER
     | labelB = IDENTIFIER 
   ;
Run Code Online (Sandbox Code Playgroud)

您还可以执行此操作来创建不同的访问:

id :
     c_type IDENTIFIER    #idType1 //choose more appropriate names!
     | IDENTIFIER         #idType2
   ;
Run Code Online (Sandbox Code Playgroud)

这将为两种替代方案创建不同的访问者,并且我认为(即尚未验证)id不会调用该访问者。

但我更喜欢以下方法:

id :
        typeDef
     |  otherId
     ;
typeDef: c_type IDENTIFIER;
otherId : IDENTIFIER ;
Run Code Online (Sandbox Code Playgroud)

这是一个类型更重的系统。但您可以非常具体地访问节点。我使用的一些经验法则:

  1. |仅当所有替代项都是解析器规则时才使用。
  2. 将每个令牌包装在解析器规则中(如otherId)以赋予它们“更多含义”。
  3. 如果标记不是很重要(例如;),因此在解析树中不需要,则可以混合解析器规则和标记。