我尝试编写一个简单的ANTLR4语法来解析SRT字幕文件。我认为这将是一个简单的介绍性任务,但我想我必须忽略一些观点。但首先要做的事情就是语法:
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
所以看起来第二行应用于词法分析器规则LINE
(因为这是它可以匹配的最长的令牌),但我期望匹配规则TSTAMP
(和这就是它在语法中LINE
规则之前定义的原因。我的ANTLR4知识在这一点上很弱,以某种方式调整语法,词法分析器可以尝试匹配令牌上的子集,具体取决于解析器规则中的当前位置。我打算实现的是匹配TSTAMP
而不是LINE
,因为TSTAMP
实际上是预期的输入。也许我可以用一些词法模式来欺骗它,但我几乎无法相信它不能以更简单的方式编写。可以吗?
正如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];
答案 0 :(得分:2)
您对令牌LINE
的定义包含所有内容:
LINE : ~('\r'|'\n')+;
每个TSTAMP
也是LINE
,但是一行可以匹配更长的词汇。你可以看到它。 ANTLR喜欢最长的比赛。
为了使你的语法有效,将决策者的词汇从词法分析器转移到解析器中:
subtitle: SUBNO NL
TSTAMP ' --> ' TSTAMP NL
line*;
line: (LINECHAR | TSTAMP | SUBNO)* NL?;
...
LINECHAR : ~('\r'|'\n' ) ; //remove the '+'
您可以看到一行可能包含LINE_CHAR
,还包含TSTAMP
和SUBNO
。