ANTLR:处理表情符号字符
ANTLR: handle emoji characters
我正在使用以下 EmojiLexer.g4 和 ANTLR 4.7.2
lexer grammar EmojiLexer;
String
: '"' StringChar* '"'
;
fragment StringChar
: ~["\r\n]
;
Punctuation
: ( '['
| ':'
| ']'
| ';'
| '@'
)
;
fragment IdentifierNonDigit
: [a-zA-Z_$\u0080-\uffff]
;
Identifier
: IdentifierNonDigit+
;
Whitespace
: [ \t]+
-> skip
;
Newline
: ( '\r' '\n'?
| '\n'
)
-> skip
;
并遵循 Java 代码来对字符串进行 lex:
public class EmojiTest {
public static void main(String[] args) {
final String string = "[foo bar:bazz];\n"
+ "\n"
+ "@\"emojis break it: \uD83D\uDE31\";\n"
+ "\n"
+ "[foo bar:bazz];\n";
final CharStream charStream = CharStreams.fromString(string);
final EmojiLexer lexer = new EmojiLexer(charStream);
while (true) {
final Token token = lexer.nextToken();
final int type = token.getType();
if (type < 0) {
break;
}
final int startIndex = token.getStartIndex();
final int stopIndex = token.getStopIndex() + 1;
System.out.println(startIndex + "-" + stopIndex + ": " + type + ": " + escape(string.substring(startIndex, stopIndex)));
}
}
private static String escape(String s) {
final StringBuilder buffer = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
final char chr = s.charAt(i);
if (chr >= 0x20 && chr < 0x7f) {
buffer.append(chr);
}
else {
buffer.append("\u");
final String hex = Integer.toHexString(chr);
for (int j = hex.length(); j < 4; j++) {
buffer.append('0');
}
buffer.append(hex);
}
}
return buffer.toString();
}
}
但不知何故,表情符号双字符混淆了 ANTLR,因为结果是:
0-1: 2: [
1-4: 3: foo
5-8: 3: bar
8-9: 2: :
9-13: 3: bazz
13-14: 2: ]
14-15: 2: ;
17-18: 2: @
18-38: 1: "emojis break it: \ud83d\ude31
38-39: 2: "
41-42: 2: \u000a
42-45: 3: [fo
46-49: 3: ba
49-50: 2: r
50-54: 3: :baz
54-55: 2: z
55-56: 2: ]
这是我的代码还是 ANTLR 中的错误?
您使用了代理项对来指定表情符号。我不确定这是否仍然受支持。而是使用完整的 UTF-32 规范:\u{1f631}
for
如果您想捕获所有表情符号,而不仅仅是这一个,您甚至可以使用:
EMOJI : [\p{Emoji}];
表情符号使用 Unicode 字符 class。
另请参阅:https://github.com/antlr/antlr4/blob/master/doc/unicode.md#unicode-code-points-in-lexer-grammars
通过用 UTF-32 字符交换代理项,我得到了这个标记列表:
[@0,0:0='[',<2>,1:0]
[@1,1:3='foo',<3>,1:1]
[@2,5:7='bar',<3>,1:5]
[@3,8:8=':',<2>,1:8]
[@4,9:12='bazz',<3>,1:9]
[@5,13:13=']',<2>,1:13]
[@6,14:14=';',<2>,1:14]
[@7,17:17='@',<2>,3:0]
[@8,18:37='"emojis break it: "',<1>,3:1]
[@9,38:38=';',<2>,3:21]
[@10,41:41='[',<2>,5:0]
[@11,42:44='foo',<3>,5:1]
[@12,46:48='bar',<3>,5:5]
[@13,49:49=':',<2>,5:8]
[@14,50:53='bazz',<3>,5:9]
[@15,54:54=']',<2>,5:13]
[@16,55:55=';',<2>,5:14]
[@17,57:56='<EOF>',<-1>,6:0]
这是我使用的输入:
[foo bar:bazz];
@"emojis break it: ";
[foo bar:bazz];
我不得不承认我用我的 ANLTR4 扩展测试了它,它使用 Typescript 而不是 Java,但我相信这不重要。
问题是 ANTLR 是 codepoint-aware 并且返回的索引是 codepoint-indices。因此,应用程序代码也需要进行调整以处理代码点。
我正在使用以下 EmojiLexer.g4 和 ANTLR 4.7.2
lexer grammar EmojiLexer;
String
: '"' StringChar* '"'
;
fragment StringChar
: ~["\r\n]
;
Punctuation
: ( '['
| ':'
| ']'
| ';'
| '@'
)
;
fragment IdentifierNonDigit
: [a-zA-Z_$\u0080-\uffff]
;
Identifier
: IdentifierNonDigit+
;
Whitespace
: [ \t]+
-> skip
;
Newline
: ( '\r' '\n'?
| '\n'
)
-> skip
;
并遵循 Java 代码来对字符串进行 lex:
public class EmojiTest {
public static void main(String[] args) {
final String string = "[foo bar:bazz];\n"
+ "\n"
+ "@\"emojis break it: \uD83D\uDE31\";\n"
+ "\n"
+ "[foo bar:bazz];\n";
final CharStream charStream = CharStreams.fromString(string);
final EmojiLexer lexer = new EmojiLexer(charStream);
while (true) {
final Token token = lexer.nextToken();
final int type = token.getType();
if (type < 0) {
break;
}
final int startIndex = token.getStartIndex();
final int stopIndex = token.getStopIndex() + 1;
System.out.println(startIndex + "-" + stopIndex + ": " + type + ": " + escape(string.substring(startIndex, stopIndex)));
}
}
private static String escape(String s) {
final StringBuilder buffer = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
final char chr = s.charAt(i);
if (chr >= 0x20 && chr < 0x7f) {
buffer.append(chr);
}
else {
buffer.append("\u");
final String hex = Integer.toHexString(chr);
for (int j = hex.length(); j < 4; j++) {
buffer.append('0');
}
buffer.append(hex);
}
}
return buffer.toString();
}
}
但不知何故,表情符号双字符混淆了 ANTLR,因为结果是:
0-1: 2: [
1-4: 3: foo
5-8: 3: bar
8-9: 2: :
9-13: 3: bazz
13-14: 2: ]
14-15: 2: ;
17-18: 2: @
18-38: 1: "emojis break it: \ud83d\ude31
38-39: 2: "
41-42: 2: \u000a
42-45: 3: [fo
46-49: 3: ba
49-50: 2: r
50-54: 3: :baz
54-55: 2: z
55-56: 2: ]
这是我的代码还是 ANTLR 中的错误?
您使用了代理项对来指定表情符号。我不确定这是否仍然受支持。而是使用完整的 UTF-32 规范:\u{1f631}
for
如果您想捕获所有表情符号,而不仅仅是这一个,您甚至可以使用:
EMOJI : [\p{Emoji}];
表情符号使用 Unicode 字符 class。
另请参阅:https://github.com/antlr/antlr4/blob/master/doc/unicode.md#unicode-code-points-in-lexer-grammars
通过用 UTF-32 字符交换代理项,我得到了这个标记列表:
[@0,0:0='[',<2>,1:0]
[@1,1:3='foo',<3>,1:1]
[@2,5:7='bar',<3>,1:5]
[@3,8:8=':',<2>,1:8]
[@4,9:12='bazz',<3>,1:9]
[@5,13:13=']',<2>,1:13]
[@6,14:14=';',<2>,1:14]
[@7,17:17='@',<2>,3:0]
[@8,18:37='"emojis break it: "',<1>,3:1]
[@9,38:38=';',<2>,3:21]
[@10,41:41='[',<2>,5:0]
[@11,42:44='foo',<3>,5:1]
[@12,46:48='bar',<3>,5:5]
[@13,49:49=':',<2>,5:8]
[@14,50:53='bazz',<3>,5:9]
[@15,54:54=']',<2>,5:13]
[@16,55:55=';',<2>,5:14]
[@17,57:56='<EOF>',<-1>,6:0]
这是我使用的输入:
[foo bar:bazz];
@"emojis break it: ";
[foo bar:bazz];
我不得不承认我用我的 ANLTR4 扩展测试了它,它使用 Typescript 而不是 Java,但我相信这不重要。
问题是 ANTLR 是 codepoint-aware 并且返回的索引是 codepoint-indices。因此,应用程序代码也需要进行调整以处理代码点。