微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

将 Lexer 中的所有文本匹配并标记为一个标记,直到 ANTLR4 中的某个字符串

如何解决将 Lexer 中的所有文本匹配并标记为一个标记,直到 ANTLR4 中的某个字符串

在 ANTLR4 中匹配任意字符串直到流包含某个多字符字符串的正确词法分析器规则是什么?

例如在 CharStream 我有

#integer12314#end
#freetextFoo bar#end

我想从令牌类型 Foo barTEXT 创建令牌。

  • 每个条目都以 #end 标记结束。
  • TEXT[\u001-\u007f]* 组成,但让我们暂时忘记空格交互。
  • TEXT 可以包含##e#en

从上面的 CharStream 我期望令牌流:

tokenOf(#integer) Integer tokenOf(#end) tokenOf(#freetext) TEXT tokenOf(#end)

显然我可以尝试在词法分析器语法中通过以下方式解决这个问题:

TEXT : [\u0001-\u007f]+? '#end'

但它也将包含结束标记并且解析器语法更丑。

(额外问题:

  • 如何正确捕获 TEXT 中的空白,但可能需要使用词法分析器模式;
  • 如何避免 Identifier : [a-zA-Z_[a-zA-Z0-9_$]* 和其他词法分析器定义的干扰。 )

解决方法

已编辑 任何在词法分析器规则中放置 + 的尝试,例如

TEXT : (NOT_END1 ...)+ ;
fragment NOT_END1 : [\u0001-"$-\u007f] ;

消耗太多。

有关 OTHER : . ; 的使用,请参阅 Bart 的回答 here

使用此文件 input.txt :

#integer12314#end
#freetext x'010203' #end
#freetext##end
#freetext#e#end
#freetext#en e n d # en nd##end
#freetext#e x'040506' #en  #end

我使用 this editor 插入 010203 和 040506 的位置:

00000000  23 69 6e 74 65 67 65 72  31 32 33 31 34 23 65 6e  |#integer12314#en|
00000010  64 0a 23 66 72 65 65 74  65 78 74 01 02 03 23 65  |d.#freetext...#e|
00000020  6e 64 0a 23 66 72 65 65  74 65 78 74 23 23 65 6e  |nd.#freetext##en|
00000030  64 0a 23 66 72 65 65 74  65 78 74 23 65 23 65 6e  |d.#freetext#e#en|
00000040  64 0a 23 66 72 65 65 74  65 78 74 23 65 6e 20 65  |d.#freetext#en e|
00000050  20 6e 20 64 20 23 20 65  6e 20 6e 64 23 23 65 6e  | n d # en nd##en|
00000060  64 0a 23 66 72 65 65 74  65 78 74 23 65 20 04 05  |d.#freetext#e ..|
00000070  06 23 65 6e 20 20 23 65  6e 64 0a                 |.#en  #end.|
0000007b

文件 Question_any.g4 :

grammar Question_any;

prog
@init {System.out.println("Question_any last update 0901");}
    :   ( line
            {System.out.println("Found line " + $line.source_line + " `" + $line.text + "`");}
        )+ EOF
    ;

line returns [int source_line]
@init {$source_line = getCurrentToken().getLine();}
    :   SHARP_INT INTEGER SHARP_END
    |   SHARP_FREE ANY+ SHARP_END
    ;

SHARP_INT  : '#integer' ;
SHARP_FREE : '#freetext' ;
SHARP_END  : '#end' ;
INTEGER    : [0-9]+ ;
NL         : [\r\n]+ -> skip ;
WS         : [ \t]+ -> channel(HIDDEN) ;

ANY        : [\u0001-\u007f] ; // must be after WS

执行:

$ grun Question_any prog -tokens input.txt 
[@0,0:7='#integer',<'#integer'>,1:0]
[@1,8:12='12314',<INTEGER>,1:8]
[@2,13:16='#end',<'#end'>,1:13]
[@3,18:26='#freetext',<'#freetext'>,2:0]
[@4,27:27='',<ANY>,2:9]
[@5,28:28='',2:10]
[@6,29:29='',2:11]
[@7,30:33='#end',2:12]
...
[@35,98:106='#freetext',6:0]
[@36,107:107='#',6:9]
[@37,108:108='e',6:10]
[@38,109:109=' ',<WS>,channel=1,6:11]
[@39,110:110='',6:12]
[@40,111:111='',6:13]
[@41,112:112='',6:14]
[@42,113:113='#',6:15]
[@43,114:114='e',6:16]
[@44,115:115='n',6:17]
[@45,116:117='  ',6:18]
[@46,118:121='#end',6:20]
[@47,123:122='<EOF>',<EOF>,7:0]
Question_any last update 0901
Found line 1 `#integer12314#end`
Found line 2 `#freetext#end`
Found line 3 `#freetext##end`
Found line 4 `#freetext#e#end`
Found line 5 `#freetext#en e n d # en nd##end`
Found line 6 `#freetext#e #en  #end`

不打印特殊字符。

,

作为临时解决方案,我选择将所有非结尾都放入 Lexer 规则中:

TEXT : (NOT_END1 | NOT_END2 | NOT_END3 | NOT_END4)+ ;

fragment NOT_END1 :       [\u0001-"$-\u007f] ;  // # is between # and $ in ASCII
fragment NOT_END2 : '#'   [\u0001-df-\u007f] ;  // e is between d and f
fragment NOT_END3 : '#e'  [\u0001-mo-\u007f] ;  // n is between m and o
fragment NOT_END4 : '#en' [\u0001-ce-\u007f] ;  // d is between c and e

END : '#end'

因为这太丑了,我对这种可耻的行为感到难过:-),我希望有更优雅的解决方案。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。