首页 > 解决方案 > 克服词法歧义

问题描述

2020 年 10 月 25 日更新

我有一个面向记录的文件格式,其中('\r'? '\n' | '\r')[ie NEWLINE] 可以是一系列字符的终止,或者单独作为两个记录之间的分隔符。每条记录中也有自由文本。

我遇到的两个问题是:

  1. 如果我在 TEXT 的定义中包含数字和标点符号,则timestamp停止识别,
  2. 我想处理将两条记录与行终止换行不同的换行符。

我想解析这个简单的文件格式:

1
00:00:01,123 --> 00:00:10,000
第一个标题在这里
第二个标题在这里

2
00:00:10,001 --> 00:00:20,200
第三个字幕缺少第四个字幕

我的语法是这样的:

语法 str;

str_file:记录+;

记录:frameLine 时间戳标题标题?新队 ;

frameLine: FRAME_ID NEWLINE ;

时间戳:TIMECODE ARROW TIMECODE NEWLINE ;

标题:文字换行;

新行: ('\r'? '\n' | '\r') ;

FRAME_ID: [0-9]+ ;

箭头:'-->';

文本:('A'..'Z'
            | 'a'..'z'
            | ' ')+ ;

时间码:[0-9][0-9] ':' [0-9][0-9] ':' [0-9][0-9] ',' [0-9][0-9 ][0-9];

重构后更新

Bart 下面的答案肯定比我在下面的重构更可靠,这似乎也有效,但可能无法解析一些输入:

语法字幕;

字幕:记录+ EOF;

记录:frameLine 时间戳标题标题?新队 ;

框架线 FRAME_ID NEWLINE ;

时间戳:TIMECODE ARROW TIMECODE NEWLINE;

标题:文字换行;

新行: ('\r'? '\n' | '\r') ;

FRAME_ID: [0-9]+ ;

箭头:'-->';

时间码:[0-9][0-9] ':' [0-9][0-9] ':' [0-9][0-9] ',' [0-9][0-9 ][0-9];

文本:(字 | 数字 | ' ' | PUNC)+;

字:[\u0041-\u007a\u00c0-\u00ff]+;

数字:('0'..'9')+;

PUNC: '/' | '*' | ':' | ', ';
 

标签: antlr4

解决方案


问题在于某些字符在特定上下文中具有不同的含义:数字可以是帧 ID、时间戳的一部分或文本的一部分。从理论上讲,时间戳之类的00:00:10,001甚至可以是一行文本的一部分。ANTLR 的词法分析器不能很好地处理这种上下文敏感性。

您可以做的是利用词法分析器模式。假设输入总是以帧 ID 开始,那么当词法分析器“看到”帧 ID 之后的换行符时,它就会进入时间模式。在这种时间模式下,您知道您将匹配用箭头分隔的时间戳,并且当您在该模式下“看到”新行时,您将进入文本模式。只要您“看到” 2 个连续的换行符,此文本模式就会结束。

快速演示:

文件:SrtLexer.g4

lexer grammar SrtLexer;

FRAME_ID
 : [0-9]+
 ;

NEW_LINE
 : NL -> skip, mode(TIME_MODE)
 ;

fragment NL :  '\r'? '\n' | '\r';
fragment S  : [ \t];
fragment D  : [0-9];

mode TIME_MODE;

  TIME_STAMP
   : D D ':' D D ':' D D ',' D D D
   ;

  ARROW
   : '-->'
   ;

  TIME_MODE_SPACE
   : S -> skip
   ;

  TIME_MODE_NEW_LINE
   : NL -> skip, mode(TEXT_MODE)
   ;

mode TEXT_MODE;

  TEXT_LINE
   : ~[\r\n]+
   ;

  TEXT_MODE_NEW_FRAME
   : NL NL -> skip, mode(DEFAULT_MODE)
   ;

  TEXT_MODE_NEW_LINE
   : NL -> skip
   ;

文件:SrtParser.g4

parser grammar SrtParser;

options {
  tokenVocab=SrtLexer;
}

parse
 : frame+ EOF
 ;

frame
 : FRAME_ID time text
 ;

time
 : TIME_STAMP ARROW TIME_STAMP
 ;

text
 : TEXT_LINE+
 ;

如果您现在解析示例输入,您将得到以下解析树:

在此处输入图像描述

请注意,这样输入如下:

1
00:00:01,123 --> 00:00:10,000
And then he typed 00:00:01,123 --> 00:00:10,000

也将被正确解析。


推荐阅读