Parsing 我可以强制ANTL4读取期望的标记,而不是让它猜测它可能是什么类型的标记吗?

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

我尝试编写一个简单的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
因此,它看起来像是应用于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没有(也就是说,我不知道怎么做)。谢谢你的提示-似乎它是更适合这种情况的工具。