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

【记录】写antrl的语法时的一个心得

ANTLR crifan 3476浏览 0评论

【背景】

想要通过antlr语法去匹配对应的形如:

"Current loop"       "|sv|""Stömslinga"

的多语言的字符串。

与此对应的,普通的字符串,就只有一个:

"Current loop"

对此,去写antlr的语法。

【折腾过程】

1.直接写成:

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
fragment
DIGIT   :   '0'..'9';
 
fragment
LETTER  :   'a'..'z' | 'A'..'Z';
 
fragment
HEX_DIGIT   : ('a'..'f'|'A'..'F' | DIGIT) ;
 
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
ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;
 
//fragment
STRING
    :  '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
    ;
 
multi_lan_str   :   STRING '"|' LETTER LETTER '|"' STRING;

虽然是可以编译通过的,但是结果是无法匹配的。

原因是:

想要用:

‘"|’ LETTER LETTER ‘|"’

去匹配:

"|sv|"

实际上是匹配不到的,因为之前的STRING这个lexer中的token,已经匹配掉了此内容了。

 

2.再换用:

1
multi_lan_str   :   STRING WS* STRING STRING;

也是不行的。

因为,其中会有多重匹配的问题:

[17:17:31] warning(200): HartEddl.g:81:16:

Decision can match input such as "STRING STRING STRING STRING" using multiple alternatives: 2, 3

As a result, alternative(s) 3 were disabled for that input

[17:17:31] warning(200): D:\DevRoot\IndustrialMobileAutomation\HandheldDataSetter\ANTLR\projects\v1.5\HartEddlParser_local_TFS\HartEddl.g:81:16:

Decision can match input such as "STRING STRING STRING STRING" using multiple alternatives: 2, 3

As a result, alternative(s) 3 were disabled for that input

 

3.而改为:

1
multi_lan_str   :   STRING WS+ STRING STRING;

是可以编译通过,但是还是无法匹配的。

原因:

此处想要通过:

WS+

匹配"Current loop" 和"|sv|"之间的空格,是匹配不到的。

因为之前有个:

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

是已经匹配到对应的那几个空格了。所以此处匹配不到了。

 

4.再去写成:

1
string_value    :   (DEFINE_IMPORTED_VALUE | STRING+);

也是会出现多重匹配的问题:

[17:20:28] warning(200): HartEddl.g:82:41:

Decision can match input such as "STRING" using multiple alternatives: 1, 2

As a result, alternative(s) 2 were disabled for that input

[17:20:28] warning(200): D:\DevRoot\IndustrialMobileAutomation\HandheldDataSetter\ANTLR\projects\v1.5\HartEddlParser_local_TFS\HartEddl.g:82:41:

Decision can match input such as "STRING" using multiple alternatives: 1, 2

As a result, alternative(s) 2 were disabled for that input

 

5.最终,折腾了半天,是通过:

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
fragment
LETTER  :   'a'..'z' | 'A'..'Z';
 
fragment
DIGIT   :   '0'..'9';
 
COMMENT
    :   '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    |   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;
 
WS  :   ( ' ' | '\t' | '\r' | '\n')+ {$channel=HIDDEN;};
 
 
//fragment
ID  :   ('a'..'z' | 'A'..'Z' |'_') ('a'..'z' | 'A'..'Z'| '0'..'9' |'_')*;
 
 
 
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
ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;
 
STR_LAN :   '"|' LETTER LETTER '|"';
 
//fragment
STRING
    :  '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
    ;
 
fragment
IMPORTED_VALUE  :   '[' ID ']'; /* normal ID with [] */
 
//fragment
DEFINE_IMPORTED_VALUE
        :   ID | IMPORTED_VALUE;
 
fragment
HEX_DIGIT   : ('a'..'f'|'A'..'F' | DIGIT) ;
//fragment
HEX_VALUE   :   '0x' HEX_DIGIT+;
 
//fragment
DECIMAL_VALUE   :   DIGIT+;
 
 
//fragment
direct_value    :   (DECIMAL_VALUE | HEX_VALUE);
 
//string_value  :   (DEFINE_IMPORTED_VALUE | multi_lan_str | STRING);
//string_value  :   (DEFINE_IMPORTED_VALUE | STRING);
//string_value  :   (DEFINE_IMPORTED_VALUE | STRING | multi_lan_str);
//string_value  :   (DEFINE_IMPORTED_VALUE | STRING+);
string_value    :   (DEFINE_IMPORTED_VALUE | STRING | multi_lan_str); // support multi language string
 
//multiple language string
//eg: "Current loop"       "|sv|""St?mslinga"
//multi_lan_str :   STRING '"|' LETTER LETTER '|"' STRING;
//multi_lan_str :   STRING WS+ STRING STRING;
multi_lan_str   :   STRING WS* STR_LAN STRING;

而解决此问题的。

其中,是单独定义了一个STR_LAN,去实现匹配符合:

"|XX|"

的格式的特殊字符串,并且:

  • STR_LAN是要放在STRING前面
  • STR_LAN是要加上fragment,表示类似于inline被替换的效果

如此,才不会和STRING冲突,才能和STRING共存。

 

如此,才能用:

1
STRING WS* STR_LAN STRING

匹配到此处形如:

"Current loop"      "|sv|""St?mslinga"

的字符串的。

 

【总结】

话说antlr的语法,还是很不咋地,很诡异。

一旦你需要实现一些特殊的,精确的匹配的效果的时候,往往就很难实现了。

转载请注明:在路上 » 【记录】写antrl的语法时的一个心得

发表我的评论
取消评论

表情

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

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