关于Java:了解Antlr4中的上下文数据结构

Understanding the context data structure in Antlr4

我正尝试在Antlr4的帮助下用Java编写代码转换器,到目前为止,在语法部分上取得了巨大的成功。但是,我现在将头撞在墙上,这使我的脑袋围绕解析树数据结构,在解析输入后我需要进行处理。

我正在尝试使用访问者模板遍历我的解析树。我将向您展示一个例子来说明我的困惑点。

我的语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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\
\
\\u000C]+ -> skip  
    ;
COMMENT
    :   '/*' .*? '*/' -> skip
    ;

LINE_COMMENT
    :   '//' ~[\
\
]* -> 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
      ;

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

1
double x: x >= c_expr

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

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

我的问题是,如何确定左边的孩子(树中的" x")是终端节点,或更具体地说是" IDENTIFIER"?终端节点没有访问规则,因为它们不是规则。
ctx.getChild(0)没有RuleIndex。我想我可以用它来检查我是否在终端机中,但是仍然不能告诉我我是在IDENTIFIER还是其他终端令牌中。我需要能够以某种方式分辨出差异。

我还有更多问题,但是在写我的解释的时间上我忘了他们:< 提前致谢。


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

1
2
3
4
id :
     c_type labelA = IDENTIFIER
     | labelB = IDENTIFIER
   ;

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

1
2
3
4
id :
     c_type IDENTIFIER    #idType1 //choose more appropriate names!
     | IDENTIFIER         #idType2
   ;

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

我更喜欢以下方法:

1
2
3
4
5
6
id :
        typeDef
     |  otherId
     ;
typeDef: c_type IDENTIFIER;
otherId : IDENTIFIER ;

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

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