我可以强制 ANTL4 读取预期的标记而不是让它猜测它可能是哪种标记吗?
Can I force ANTL4 to read expected tokens instead of letting it guessing what kind of token it may be?
我尝试编写一个简单的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 知识薄弱,无法以某种方式调整语法,词法分析器可能会根据 parser 规则中的当前位置尝试匹配标记上的子集。我打算实现的是匹配 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];
您对令牌的定义 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
。
我尝试编写一个简单的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 知识薄弱,无法以某种方式调整语法,词法分析器可能会根据 parser 规则中的当前位置尝试匹配标记上的子集。我打算实现的是匹配 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];
您对令牌的定义 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
。