我可以强制ANTL4读取预期的令牌而不是让它猜测它可能是什么样的令牌吗?

时间:2015-05-31 15:19:15

标签: 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 ',' 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];

1 个答案:

答案 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,还包含TSTAMPSUBNO