如何在空行处关闭ANTLR词法模式?
ANTLR词法分析器实现:捕获含换行文本块,遇空行退出模式
问题背景
输入文件包含多种以空行分隔的数据块,需为每个块启用对应词法模式处理,其中部分块需要捕获包含换行符在内的所有内容。但现有词法规则无法实现:捕获包含单个换行的所有字符,同时在遇到连续换行(空行)时退出当前模式,尝试通过指定允许字符并排除单个字符的方式未能达成需求。
初始实现代码
初始词法分析器语法BlocksLexer.g4
lexer grammar BlocksLexer ; BLK01 : 'BLOCK01' ; BLK02 : 'BLOCK02' ; BLK03 : 'BLOCK03' ; BLK04 : 'BLOCK04' -> pushMode(ALLTEXT) ; BLK05 : 'BLOCK05' ; KEYPARPAR : 'param' ; KEYPARPRO1 : 'property1' ; KEYPARPRO2 : 'property2' ; KEYPARSIZ : 'size' ; KEYPARWGT : 'weight' ; KEYPARPAG : 'pages' ; KEYPARCNT : 'count' ; KEYPARCUR : 'currency' ; KEYPARVAL : 'value' ; ARROW : '->' ; CHRAMP : '&' ; CHRAPOSDOU : '"' ; CHRAPOSSIN : '\'' ; CHRCOLON : ':' ; CHRCOMMA : ',' ; CHRDOT : '.' ; CHREQUAL : '=' ; CHRSEMICOLON : ';' ; ID : [A-Z][A-Z0-9]+ ; NUM : [0-9]+ ; STRING : '"' (~'\r'|~'\n')*? '"' ; NAM : [A-Z][a-z]+ ; NAMVAR : [a-z][a-z]* ; NEWLINE : ('\r'? '\n'|'\r') -> channel(HIDDEN) ; WS : [ \t]+ -> skip ; // Parsing novel mode ALLTEXT; NOVEL : ~[#]+ ; CLOSE_ALLTEXT : '#' -> popMode ;
初始解析器语法BlocksParser.g4
parser grammar BlocksParser ; options { tokenVocab = BlocksLexer ; } file : blk01 blk02 blk03 blk04+ blk05 ; blk01 : blk01Who blk01What ; blk01Who : ARROW BLK01 ; blk01What : ID assign ; blk02 : blk02Who blk02What ; blk02Who : ARROW BLK02 ; blk02What : ID assign assign* ; blk03 : blk03Who blk03What+ ; blk03Who : ARROW BLK03 ; blk03What : ID assign assign* ; blk04 : blk04Who blk04What CLOSE_ALLTEXT; blk04Who : ARROW BLK04 ; blk04What : NOVEL ; blk05 : blk05Who blk05What+ ; blk05Who : ARROW BLK05 ; blk05What : ID assign assign* ; assign : param (CHRCOLON|CHREQUAL) value ; param : KEYPARPAR|KEYPARPRO1|KEYPARPRO2|KEYPARSIZ|KEYPARWGT|KEYPARPAG|KEYPARCNT|KEYPARCUR|KEYPARVAL ; value : STRING|NUM|var ; var : CHRAMP NAMVAR ;
初始测试输入文件t.txt
->BLOCK01 START param=&wert ->BLOCK02 SELF property1="abc" property2="def" ->BLOCK03 ITEM1 property1:"xyz" property2:"set" ITEM2 property1:"xyz" property2:"set" ITEM3 property1:"xyz" property2:"set" ->BLOCK04 GOOD01 size:"2x3" weight:"23" DOCUMENT01 pages:37 ORDER01 count:37 # ->BLOCK04 GOOD02 size:"5x6" weight:"32" AMOUNT02 currency:"US" value:2033 # ->BLOCK04 ORDER03 count:44 DOCUMENT03 pages:24 GOOD03 size:"2x4" weight:"87" # ->BLOCK05 GOOD0501 size:"3x7" weight:"34" AMOUNT0501 currency:"EU" value:2025
初始实现用#作为BLOCK04的终止符演示功能,需求是改为以空行作为ALLTEXT词法模式的终止符。
解决方案
采用Kens(kaby76)的方法修改语法,实现以空行终止ALLTEXT模式的需求。
修改后词法分析器语法BlckVer2Lexer.g4
lexer grammar BlckVer2Lexer ; tokens { NOVEL } BLK01 : 'BLOCK01' ; BLK02 : 'BLOCK02' ; BLK03 : 'BLOCK03' ; BLK04 : 'BLOCK04' -> pushMode(ALLTEXT) ; BLK05 : 'BLOCK05' ; KEYPARPAR : 'param' ; KEYPARPRO1 : 'property1' ; KEYPARPRO2 : 'property2' ; KEYPARSIZ : 'size' ; KEYPARWGT : 'weight' ; KEYPARPAG : 'pages' ; KEYPARCNT : 'count' ; KEYPARCUR : 'currency' ; KEYPARVAL : 'value' ; ARROW : '->' ; CHRAMP : '&' ; CHRAPOSDOU : '"' ; CHRAPOSSIN : '\'' ; CHRCOLON : ':' ; CHRCOMMA : ',' ; CHRDOT : '.' ; CHREQUAL : '=' ; CHRSEMICOLON : ';' ; ID : [A-Z][A-Z0-9]+ ; NUM : [0-9]+ ; STRING : '"' ~[\n]*? '"' ; NAM : [A-Z][a-z]+ ; NAMVAR : [a-z][a-z]* ; NEWLINE : '\n' -> channel(HIDDEN) ; WS : [ \r\t]+ -> skip ; // Parsing novel mode ALLTEXT; DUMMY1 : ~[\n] -> more ; DUMMY2 : { this.getCharPositionInLine() != 0 }? '\n' -> more ; DUMMY3 : { this.getCharPositionInLine() == 0 }? '\n' -> popMode, type(NOVEL) ;
修改后解析器语法BlckVer2Parser.g4
parser grammar BlckVer2Parser ; options { tokenVocab = BlckVer2Lexer ; } file : blk01 blk02 blk03 blk04+ blk05 EOF ; blk01 : blk01Who blk01What ; blk01Who : ARROW BLK01 ; blk01What : ID assign ; blk02 : blk02Who blk02What ; blk02Who : ARROW BLK02 ; blk02What : ID assign assign* ; blk03 : blk03Who blk03What+ ; blk03Who : ARROW BLK03 ; blk03What : ID assign assign* ; blk04 : blk04Who blk04What ; blk04Who : ARROW BLK04 ; blk04What : NOVEL ; blk05 : blk05Who blk05What+ ; blk05Who : ARROW BLK05 ; blk05What : ID assign assign* ; assign : param (CHRCOLON|CHREQUAL) value ; param : KEYPARPAR|KEYPARPRO1|KEYPARPRO2|KEYPARSIZ|KEYPARWGT|KEYPARPAG|KEYPARCNT|KEYPARCUR|KEYPARVAL ; value : STRING|NUM|var ; var : CHRAMP NAMVAR ;
修改后测试输入文件
->BLOCK01 START param=&wert ->BLOCK02 SELF property1="abc" property2="def" ->BLOCK03 ITEM1 property1:"xyz" property2:"set" ITEM2 property1:"xyz" property2:"set" ITEM3 property1:"xyz" property2:"set" ->BLOCK04 GOOD01 size:"2x3" weight:"23" DOCUMENT01 pages:37 ORDER01 count:37 ->BLOCK04 GOOD02 size:"5x6" weight:"32" AMOUNT02 currency:"US" value:2033 ->BLOCK04 ORDER03 count:44 DOCUMENT03 pages:24 GOOD03 size:"2x4" weight:"87" ->BLOCK05 GOOD0501 size:"3x7" weight:"34" AMOUNT0501 currency:"EU" value:2025
效果说明
修改后可成功捕获BLOCK04的所有数据,例如第一个BLOCK04捕获到的内容为:
\nGOOD01 size:"2x3" weight:"23"\nDOCUMENT01 pages:37\nORDER01 count:37\n\n
目前需要预先将Windows换行符(\r\n)转换为Unix换行符(\n),后续可进一步适配\r\n格式。
内容的提问来源于stack exchange,提问作者Michael Rachow
相关产品推荐
相关产品推荐

