Parsing 我可以强制ANTL4读取期望的标记,而不是让它猜测它可能是什么类型的标记吗?
我尝试编写一个简单的ANTLR4语法来解析SRT字幕文件。我原以为这将是一项简单的介绍性任务,但我想我一定错过了一些要点。但首先,语法:Parsing 我可以强制ANTL4读取期望的标记,而不是让它猜测它可能是什么类型的标记吗?,parsing,grammar,antlr4,Parsing,Grammar,Antlr4,我尝试编写一个简单的ANTLR4语法来解析SRT字幕文件。我原以为这将是一项简单的介绍性任务,但我想我一定错过了一些要点。但首先,语法: grammar Srt; file : subtitle (NL NL subtitle)* EOF; subtitle: SUBNO NL TSTAMP ' --> ' TSTAMP NL LINE (NL LINE)*; TSTAMP : I99 ':' I59 ':' I59
grammar Srt;
file : subtitle (NL NL subtitle)* EOF;
subtitle: SUBNO NL
TSTAMP ' --> ' TSTAMP NL
LINE (NL LINE)*;
TSTAMP : I99 ':' I59 ':' I59 ',' I999;
SUBNO : D09+;
NL : '\r'? '\n';
LINE : ~('\r'|'\n')+;
fragment I999 : D09 D09 D09;
fragment I99 : D09 D09;
fragment I59 : D05 D09;
fragment D09 : [0-9];
fragment D05 : [0-5];
下面是SRT文件的开头,其中出现了问题:
1
00:00:20,000 --> 00:00:26,000
我得到的错误是:
line 2:0 mismatched input '00:00:20,000 --> 00:00:26,000' expecting TSTAMP
因此,它看起来像是应用于lexer规则line
(因为这是它可以匹配的最长标记),然而我期望的是匹配规则TSTAMP
(这就是为什么它在语法中定义在line
规则之前)。我的ANTLR4知识在这一点上很薄弱,无法在某种程度上调整语法,lexer可以根据解析器规则中的当前位置,尝试匹配标记上的子集。我想要实现的是匹配TSTAMP
,而不是行
,因为TSTAMP
实际上是预期的输入。也许我可以用一些lexer模式来欺骗它,但我很难相信它不能用一种更简单的方式来写。可以吗
正如CoronA所建议的,技巧是将
LINE
规则的决定推迟到解析器,这就是线索。我对语法做了更多修改,现在它顺利地添加了字幕:
grammar Srt;
file : subtitle (NL NL subtitle)* EOF;
subtitle: SUBNO NL
TSTAMP ' --> ' TSTAMP NL
lines;
lines : line (NL line)*;
line : (LINECHAR | SUBNO | TSTAMP)*;
TSTAMP : I99 ':' I59 ':' I59 ',' I999;
SUBNO : D09+;
NL : '\r'? '\n';
LINECHAR: ~[\r\n];
fragment I999 : D09 D09 D09?;
fragment I99 : D09 D09;
fragment I59 : D05 D09;
fragment D09 : [0-9];
fragment D05 : [0-5];
您对令牌
行的定义包含了所有内容:
LINE : ~('\r'|'\n')+;
每个TSTAMP
也是一个行
,但一行可以匹配更长的词条。正如你所看到的。ANTLR更喜欢最长的比赛
要使语法正常工作,请将一行是什么的决策从lexer转移到解析器中:
subtitle: SUBNO NL
TSTAMP ' --> ' TSTAMP NL
line*;
line: (LINECHAR | TSTAMP | SUBNO)* NL?;
...
LINECHAR : ~('\r'|'\n' ) ; //remove the '+'
您可以看到,一行可能包含任何line\u CHAR
,但也包含TSTAMP
s和SUBNO
s。这就是问题所在-line
实际上可以是一切,事实上它可以(为什么不?)包含TSTAMP
或任何其他“标记”。结构如副标题
规则:SUBNO NL TSTAMP'-->“TSTAMP NL LINE(NL LINE)*
所述。所以,行是“NL-s之间的所有内容”,尽管它应该在使用TSTAMP
s的行之后。如您所见,两个subtitle
用两个NL-s分隔。不使用ANTLR4编写解析器/词法分析器非常容易,但我开始相信(使用ANTLR)编码规则是不可能的,因为解析器规则强制执行某些词法分析器规则(取决于当前上下文)?我调整了我的建议。诀窍是将行
的决定推迟到解析器。谢谢,它成功了。虽然。。。如果知道输入的内容,是否有可能以更简单的方式定义规则,比如“读取每个字符直到这个字符”?目前,line正在搜索诸如TSTAMP或SUBNO之类的标记,但知道传入数据的含义,实际上没有必要尝试从输入中提取这些标记。还有其他解析器(如Parboach之类的PEG解析器)允许您使用此类规范。ANTLR没有(也就是说,我不知道怎么做)。谢谢你的提示-似乎它是更适合这种情况的工具。