ANTLR4如何在词法分析器子规则/片段中覆盖文本
ANTLR续行标记处理问题与解决方案
问题描述
我要解析的语法规定第71列是续行指示符,标识符、字面量等几乎所有内容都支持跨行续写。我的需求是移除续行标记相关字符,只保留标识符本身,但使用当前ANTLR语法时,LINE_CONTINUATION规则里的setText("")完全不起作用,导致最终生成的IDENTIFIER token被续行的注释、换行等内容污染。
当前使用的ANTLR代码如下:
IDENTIFIER : {getCharPositionInLine() < 71 }? IDENTIFIER_PART ( {getCharPositionInLine() < 71 }? IDENTIFIER_PART | LINE_CONTINUATION )* ; fragment IDENTIFIER_PART: (LETTER|DIGIT|'_'); fragment DIGIT: [0-9]; fragment LETTER options { caseInsensitive=true; } : [A-Z]; // 续行规则:第72列(即第71索引位)非空白,后续内容直到行尾,下一行从第16列开始的内容 LINE_CONTINUATION : {getCharPositionInLine() == 71 }? ~[ ] ~[\r\n]* EOL ({getCharPositionInLine() <= 15 }? [ ] )+ {setText("");} ;
我还想确认:是否可以像修改根规则文本那样,覆盖子规则(或片段)的文本值?
示例对比
输入文本
AAAAAAAAAAAA,BBBBBBBBBBB,CCCCCCCCCCCCCCCCC,DDDDDDDDDDD,EEEEEEEEEE,FFFF* Some comment FFFF,GGGGGGGG
期望的Token文本
AAAAAAAAAAAA BBBBBBBBBBB CCCCCCCCCCCCCCCCC DDDDDDDDDDD EEEEEEEEEE FFFFFFFF GGGGGGGG
实际得到的Token文本
AAAAAAAAAAAA BBBBBBBBBBB CCCCCCCCCCCCCCCCC DDDDDDDDDDD EEEEEEEEEE FFFF* Some comment\nFFFF GGGGGGGG
原因分析与解决方案
问题根源
ANTLR中,子规则的setText()仅会修改自身Token的文本,但当子规则被嵌套在父规则中时,父规则的最终文本是直接拼接所有匹配到的字符序列,不会继承子规则的文本修改。因此LINE_CONTINUATION里的setText("")对IDENTIFIER的最终文本完全没有影响。
可行方案
方案1:直接在IDENTIFIER规则中忽略续行标记字符
把续行的匹配逻辑直接嵌入IDENTIFIER规则,匹配到续行标记时不将其加入Token文本:
IDENTIFIER : {getCharPositionInLine() < 71 }? IDENTIFIER_PART ( {getCharPositionInLine() < 71 }? IDENTIFIER_PART | {getCharPositionInLine() == 71}? ~[ ] ~[\r\n]* EOL ({getCharPositionInLine() <= 15}? [ ] )+ )* ; fragment IDENTIFIER_PART: (LETTER|DIGIT|'_'); fragment DIGIT: [0-9]; fragment LETTER options { caseInsensitive=true; } : [A-Z];
方案2:保留LINE_CONTINUATION规则,手动清理IDENTIFIER文本
如果想保留LINE_CONTINUATION的独立定义,可以在匹配到它时,手动从IDENTIFIER的文本中移除续行部分:
IDENTIFIER : {getCharPositionInLine() < 71 }? IDENTIFIER_PART ( {getCharPositionInLine() < 71 }? IDENTIFIER_PART | lc=LINE_CONTINUATION { setText(getText().replace(lc.getText(), "")); } )* ; fragment IDENTIFIER_PART: (LETTER|DIGIT|'_'); fragment DIGIT: [0-9]; fragment LETTER options { caseInsensitive=true; } : [A-Z]; LINE_CONTINUATION : {getCharPositionInLine() == 71 }? ~[ ] ~[\r\n]* EOL ({getCharPositionInLine() <= 15 }? [ ] )+ ;
两种方案都能实现需求,最终生成的IDENTIFIER Token文本会自动移除续行标记内容,只保留原始标识符字符。
内容的提问来源于stack exchange,提问作者Alistair McLennan
相关产品推荐
相关产品推荐

