最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【基本解决】antlr v3,用包含{$channel=HIDDEN;}语法,结果解析出错:MissingTokenException

ANTLR crifan 3086浏览 0评论

【问题】

折腾:

【基本解决】antlr v3中包含{skip();}的语法,调试解析时出错:org.antlr.runtime.EarlyExitException

的过程中,把语法改为:

1
2
3
4
5
BLANKS  :   (' '|'\t')+ {$channel=HIDDEN;};
 
 
startParse  :   manufacture deviceType deviceRevison ddRevision;
manufacture :   'MANUFACTURER'^     BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;

 

结果,虽然是可以正常识别数值了,但是却又出现了MissingTokenException:

chanel hidden error MissingTokenException

 

【解决过程】

1.此处,很明显,还是没有完全搞懂:

1
{skip();}

1
{$channel=HIDDEN;}

的语法的含义。

2.参考:

cannot debug simple channel flag in ANTLR with Eclipse

没啥帮助,其是把

1
{$channel = HIDDEN;}

误写成:

1
($channel = HIDDEN;)

了。我此处不存在这等语法问题。

3。参考:

MissingTokenException

看起来像是,如果本身语法写的不好,变成:

不是context-free

那么就会导致此类问题。

所以,再回去,检查一下语法,看看自己能否看出一些端倪。

4.改为:

1
2
3
4
5
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
BLANKS  :   ' '+ {$channel=HIDDEN;};
 
startParse  :   manufacture deviceType deviceRevison ddRevision;
manufacture :   'MANUFACTURER'^     BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;

试试,结果错误依旧,还是MissingTokenException。

5.怀疑,现在是

BLANK+

BLANKS,两者冲突了,所以,去把现在的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BLANK   :   (' '|'\t') {$channel=HIDDEN;};
 
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
BLANKS  :   ' '+ {$channel=HIDDEN;};
 
 
//startParse    :   include* identification+;
//startParse    :   include+ identification+;
//startParse    :   identification+;
startParse  :   manufacture deviceType deviceRevison ddRevision;
manufacture :   'MANUFACTURER'^     BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^     (BLANK+! (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceType  :   'DEVICE_TYPE'^      BLANK+ (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceRevison   :   'DEVICE_REVISION'^  BLANK+ (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;
ddRevision  :   'DD_REVISION'^      BLANK+ (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;

改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//BLANK :   (' '|'\t') {$channel=HIDDEN;};
 
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
BLANKS  :   ' '+ {$channel=HIDDEN;};
 
 
//startParse    :   include* identification+;
//startParse    :   include+ identification+;
//startParse    :   identification+;
startParse  :   manufacture deviceType deviceRevison ddRevision;
manufacture :   'MANUFACTURER'^     BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^     (BLANK+! (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceType  :   'DEVICE_TYPE'^      BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceRevison   :   'DEVICE_REVISION'^  BLANKS (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;
ddRevision  :   'DD_REVISION'^      BLANKS (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;

试试,结果错误依旧。

6.后来,倒是,改为skip的形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
BLANKS  :   (' '|'\t')+ {skip();};
//BLANKS    :   ' '+ {$channel=HIDDEN;};
 
 
//startParse    :   include* identification+;
//startParse    :   include+ identification+;
//startParse    :   identification+;
startParse  :   manufacture deviceType deviceRevison ddRevision;
manufacture :   'MANUFACTURER'^     BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^     (BLANK+! (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceType  :   'DEVICE_TYPE'^      BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceRevison   :   'DEVICE_REVISION'^  BLANKS (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;
ddRevision  :   'DD_REVISION'^      BLANKS (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;

结果也是,错误依旧。

7.把中间的空格去掉,变成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
BLANKS  :   (' '|'\t')+ {skip();};
//BLANKS    :   ' '+ {$channel=HIDDEN;};
 
 
//startParse    :   include* identification+;
//startParse    :   include+ identification+;
//startParse    :   identification+;
startParse  :   manufacture deviceType deviceRevison ddRevision;
manufacture :   'MANUFACTURER'^ BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^     (BLANK+! (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceType  :   'DEVICE_TYPE'^      BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceRevison   :   'DEVICE_REVISION'^  BLANKS (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;
ddRevision  :   'DD_REVISION'^      BLANKS (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;

试试,结果错误依旧。

说明不是写语法时候的多余的空格或tab引起的。

8.难道是,前面的语法中的

DIGIT和HEX_DIGIT有冲突?

对应的定义是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fragment
DIGIT
    :   '0'..'9';
 
//FAKE_TOKEN    :   '1' '2' '3';
 
/*
DECIMAL_VALUE
    :   '1'..'9' DIGIT*;
*/
 
DECIMAL_VALUE
    :   DIGIT*;
 
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;

那么就去掉重复的定义,改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fragment
DIGIT
    :   '0'..'9';
 
//FAKE_TOKEN    :   '1' '2' '3';
 
/*
DECIMAL_VALUE
    :   '1'..'9' DIGIT*;
*/
 
DECIMAL_VALUE
    :   DIGIT*;
 
//HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
HEX_DIGIT : (DIGIT|'a'..'f'|'A'..'F') ;
 
 
HEX_VALUE
    :   '0x' HEX_DIGIT+;

试试,结果错误依旧。

9.参考了:

[antlr-interest] C Runtime problem with $channel=HIDDEN and SKIP()

难道是,此处的Java版本的,

{$channel=HIDDEN;}

也是有bug,所以才导致MissingTokenException的?

10.后来找到此MissingTokenException错误,是3.1版本中新加的:

Errors and warnings

为了更好的提供错误的详细信息的。

11.再去改为:

1
2
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
BLANKS  :   ((' '|'\t')+) {$channel=HIDDEN;};

试试,结果错误依旧。

12.后来仔细去查看了一下,关于MissingTokenException的错误的产生的过程:

see events for error of recognition exception MissingTokenException

觉得,好像MissingTokenException的产生,是多次检索此处的值0x1E6D11之后,而产生的。

换句话说,好像此处的MissingTokenException,和前面的

1
BLANKS  :   (' '|'\t')+ {$channel=HIDDEN;};

没啥关系,而是和后面的语法有关系。

所以,就去研究看看后面的语法:

1
(DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;

是不是哪里写的不妥。

13.先把感叹号去掉:

1
2
3
//manufacture   :   'MANUFACTURER'^ BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^     (BLANK+! (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
manufacture :   'MANUFACTURER'^ BLANKS (DECIMAL_VALUE | HEX_VALUE) ','? WS*;

试试,结果错误依旧。

14.怀疑是不是DECIMAL_VALUE或者HEX_VALUE写的有问题。

所以去改为:

1
2
//DECIMAL_VALUE :   DIGIT*;
DECIMAL_VALUE   :   DIGIT+;

试试,结果错误依旧。

15.去把HEX_VALUE和DECIMAL_VALUE顺序换一个:

1
2
//manufacture   :   'MANUFACTURER'^ BLANKS (DECIMAL_VALUE | HEX_VALUE) ','? WS*;
manufacture :   'MANUFACTURER'^ BLANKS (HEX_VALUE | DECIMAL_VALUE) ','? WS*;

试试,结果错误依旧。

16.把WS的skip换为hidden:

1
2
//fragment WS  :   ( ' ' | '\t' | '\r' | '\n') {skip();};
fragment WS  :   ( ' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};

试试,结果直接出错:

[14:05:28] D:\DevRoot\IndustrialMobileAutomation\HandheldDataSetter\ANTLR\projects\v1.5\DDParserDemo\output\DDParserDemoLexer.java:593: error: cannot find symbol

[14:05:28]             _channel=HIDDEN;

[14:05:28]             ^

[14:05:28]   symbol:   variable _channel

[14:05:28]   location: class DDParserDemoLexer

[14:05:28] 1 error

17.所以再把fragment去掉:

1
2
3
//fragment WS  :   ( ' ' | '\t' | '\r' | '\n') {skip();};
//fragment WS  :   ( ' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
WS  :   ( ' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};

试试,结果错误依旧,还是MissingTokenException。

18.再去仔细研究后发现, 好像还是,在识别数字0x1E6D11之前,发生的MissingTokenException,所以,还是要去折腾BLANKS。

改为:

1
2
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
BLANKS  :   (' '|'\t')+;

试试,结果,最终,才算是,正常识别空格:

can recognize blanks ok when no skip or hidden

但是很是诡异的是,为何,此处无法给多个空格,添加对应的skip()或hidden呢?

19.所以,再去把BLANKS改为BLANK,同时添加hidden:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BLANK   :   (' '|'\t') {$channel=HIDDEN;};
//BLANKS    :   (' '|'\t')+ {skip();};
//BLANKS    :   ' '+ {$channel=HIDDEN;};
 
//startParse    :   include* identification+;
//startParse    :   include+ identification+;
//startParse    :   identification+;
startParse  :   manufacture deviceType deviceRevison ddRevision;
//manufacture   :   'MANUFACTURER'^ BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^     (BLANK+! (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^ BLANKS (DECIMAL_VALUE | HEX_VALUE) ','? WS*;
manufacture :   'MANUFACTURER'^     BLANK+ (HEX_VALUE | DECIMAL_VALUE) ','? WS*;
deviceType  :   'DEVICE_TYPE'^      BLANK+ (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceRevison   :   'DEVICE_REVISION'^  BLANK+ (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;
ddRevision  :   'DD_REVISION'^      BLANK+ (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;

试试,结果又回到了开始的那个org.antlr.runtime.EarlyExitException的错误了:

if use single blank with hidden then still EarlyExitException

所以,没法这么用。

20.然后再去试试skip:

1
2
//BLANK :   (' '|'\t') {$channel=HIDDEN;};
BLANK   :   (' '|'\t') {skip();};

结果报错:

1
[14:27:15] error(208): DDParserDemo.g:119:1: The following token definitions can never be matched because prior tokens match the same input: BLANK

去看了下,应该是,已经有的WS,同样匹配此等输入了,所以,去改为:

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
/*
BLANKSPACE_TAB
//  :   (' ' | '\t'){skip();};
    :   (' ' | '\t')
    {$channel=HIDDEN;};
*/ 
//fragment BLANK    :   (' '|'\t')+ {skip();};
//BLANK :   (' '|'\t') {skip();};
//BLANK :   (' '|'\t');
//BLANK :   (' '|'\t') {$channel=HIDDEN;};
 
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
//BLANKS    :   (' '|'\t')+;
//BLANK :   (' '|'\t') {$channel=HIDDEN;};
//BLANK :   (' '|'\t') {skip();};
//BLANKS    :   (' '|'\t')+ {skip();};
//BLANKS    :   ' '+ {$channel=HIDDEN;};
 
//startParse    :   include* identification+;
//startParse    :   include+ identification+;
//startParse    :   identification+;
startParse  :   manufacture deviceType deviceRevison ddRevision;
//manufacture   :   'MANUFACTURER'^ BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^     (BLANK+! (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^ BLANKS (DECIMAL_VALUE | HEX_VALUE) ','? WS*;
manufacture :   'MANUFACTURER'^     WS+ (HEX_VALUE | DECIMAL_VALUE) ','? WS*;
deviceType  :   'DEVICE_TYPE'^      WS+ (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceRevison   :   'DEVICE_REVISION'^  WS+ (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;
ddRevision  :   'DD_REVISION'^      WS+ (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;

试试,结果仍是EarlyExitException的问题。

所以,貌似还是不能在此处使用skip或者hidden。

21.最后,还是通过:

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
131
132
133
134
135
136
137
138
139
140
grammar DDParserDemo;
 
options {
    output = AST;
    ASTLabelType = CommonTree; // type of $stat.tree ref etc...
}
 
//NEWLINE :   '\r'? '\n' ;
//NEWLINE :   '\r' '\n' ;
fragment
NEWLINE :   '\r'? '\n' ;
 
 
fragment
ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
    ;
     
fragment
FLOAT
    :   ('0'..'9')+ '.' ('0'..'9')* EXPONENT?
    |   '.' ('0'..'9')+ EXPONENT?
    |   ('0'..'9')+ EXPONENT
    ;
 
COMMENT
    :   '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    |   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;
 
//fragment WS  :   ( ' ' | '\t' | '\r' | '\n') {skip();};
//fragment WS  :   ( ' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
WS  :   ( ' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
 
STRING
    :  '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
    ;
 
CHAR:  '\'' ( ESC_SEQ | ~('\''|'\\') ) '\''
    ;
 
fragment
EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
 
 
ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;
 
fragment
OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;
 
fragment
UNICODE_ESC
    :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ;
 
fragment
DIGIT
    :   '0'..'9';
 
//FAKE_TOKEN    :   '1' '2' '3';
 
/*
DECIMAL_VALUE
    :   '1'..'9' DIGIT*;
*/
 
//DECIMAL_VALUE :   DIGIT*;
DECIMAL_VALUE   :   DIGIT+;
 
//HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
HEX_DIGIT : (DIGIT|'a'..'f'|'A'..'F') ;
 
 
HEX_VALUE
    :   '0x' HEX_DIGIT+;
 
fragment
HEADER_FILENAME
    :   ('a'..'z'|'A'..'Z') ('a'..'z'|'A'..'Z'|'_')*;
 
/*
//singleInclude :   '#include' ' '+ '"' ID '.h"' ;
//singleInclude :   '#include' ' '+ '"' ID+ '.h"' ;
//singleInclude :   '#include' ' '+ '"' HEADER_FILENAME '.h"';
//singleInclude :   '#include' ' ' '"' HEADER_FILENAME '.h"';
//singleInclude :   '#include "' HEADER_FILENAME '.h"';
//fragment singleInclude    :   '#include' (' ')+ '"' ID '.h"';
//singleInclude :   '#include' (' '|'\t')+ '""' ID '.h"';
//singleInclude :   '#include' (' '|'\t')+ '"std_defs.h"';
singleInclude   :   '#include' (' '|'\t')+  ID '.h';
 
include     :   singleInclude WS*   -> singleInclude;
*/
 
 
 
/*
BLANKSPACE_TAB
//  :   (' ' | '\t'){skip();};
    :   (' ' | '\t')
    {$channel=HIDDEN;};
*/ 
//fragment BLANK    :   (' '|'\t')+ {skip();};
//BLANK :   (' '|'\t') {skip();};
//BLANK :   (' '|'\t');
//BLANK :   (' '|'\t') {$channel=HIDDEN;};
 
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
//BLANKS    :   (' '|'\t')+ {$channel=HIDDEN;};
//BLANKS    :   (' '|'\t')+;
//BLANK :   (' '|'\t') {$channel=HIDDEN;};
//BLANK :   (' '|'\t') {skip();};
BLANKS  :   (' '|'\t')+;
//BLANKS    :   (' '|'\t')+ {skip();};
//BLANKS    :   ' '+ {$channel=HIDDEN;};
 
//startParse    :   include* identification+;
//startParse    :   include+ identification+;
//startParse    :   identification+;
startParse  :   manufacture deviceType deviceRevison ddRevision;
//manufacture   :   'MANUFACTURER'^ BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^     (BLANK+! (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
//manufacture   :   'MANUFACTURER'^ BLANKS (DECIMAL_VALUE | HEX_VALUE) ','? WS*;
manufacture :   'MANUFACTURER'^     BLANKS (HEX_VALUE | DECIMAL_VALUE) ','? WS*;
deviceType  :   'DEVICE_TYPE'^      BLANKS (DECIMAL_VALUE | HEX_VALUE) (','?)! WS*;
deviceRevison   :   'DEVICE_REVISION'^  BLANKS (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;
ddRevision  :   'DD_REVISION'^      BLANKS (DECIMAL_VALUE | HEX_VALUE)(','?)! WS*;
     
//identification    :   definiton WS* (','?)! WS*   -> definiton;
     
//definiton :   (ID)^ ('\t'!|' '!)+ (DECIMAL_VALUE | HEX_VALUE)
//definiton :   (ID)^ BLANKSPACE_TAB+ (DECIMAL_VALUE | HEX_VALUE)
//definiton :   ID ('\t'!|' '!)+ (DECIMAL_VALUE | HEX_VALUE);

去匹配:

1
2
3
4
MANUFACTURER      0x1E6D11,
DEVICE_TYPE       0x00FF,
DEVICE_REVISION   5,
DD_REVISION       1

然后得到如下树结构:

parse tree ok for define blanks and use blanks

use blank and blanks to parse the tree value

 

【总结】

1.对于匹配空格或Tab,无法使用skip()或者$channel=HIDDEN,否则,会导致无法正常解析。

2.不能在已经定义好了WS的情况下,再次单独定义单个的BLANK为空格或Tab,否则会导致重复定义,会报错:

The following token definitions can never be matched because prior tokens match the same input: BLANK

3.最终只能使用,单独定义BLANKS:

1
BLANKS  :   (' '|'\t')+;

然后在后面使用:

1
manufacture :   'MANUFACTURER'^     BLANKS (HEX_VALUE | DECIMAL_VALUE) ','? WS*;

如此:

  • 才能正常识别输入的内容,包括空格;
  • 但是识别出来的空格,就没法实现hidden或skip的效果了。目前貌似没法实现此效果。

 


【后记】

1.后来,看到这个:

what is wrong with this grammar

感觉那人说的有理,我感觉可能也是:

此MissingTokenException,可能是antlr(或antlrworks)的bug。

毕竟,语法上,貌似都没有问题,并且也都可以正常执行代码,不应该报此错误才对。

当然,有待更清楚人的来确认一下。是不是bug。

转载请注明:在路上 » 【基本解决】antlr v3,用包含{$channel=HIDDEN;}语法,结果解析出错:MissingTokenException

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
82 queries in 0.281 seconds, using 22.23MB memory