回到lex的旧位置

时间:2017-05-30 15:43:35

标签: flex-lexer lex

在我的lex处理过程中,我需要返回lex输入文件,使用不同的本地设置多次处理相同的输入。

然而,仅仅执行fseek(yyin, old_pos, SEEK_SET);不起作用,因为输入数据由lex缓冲。我怎么能(便携地)处理这个?

我尝试在YY_FLUSH_BUFFER之后添加fseek(),但它没有帮助,因为旧文件位置不正确(填充缓冲区后设置为点,而不是我评估令牌的地方。)

1 个答案:

答案 0 :(得分:2)

YY_FLUSH_BUFFER()yyseek(yyin, position, SEEK_SET)的组合(按任意顺序排列,但我先执行YY_FLUSH_BUFFER())肯定会导致从position开始扫描下一个标记。问题在于找出position的正确值。

跟踪字符偏移量相对简单(但如果您需要可在非Posix平台(如Windows)上运行的便携式扫描仪,请参阅下面的免责声明):

%{
  long scan_position = 0;
%}

%%
[[:space:]]*      scan_position += yyleng;
"some pattern"    { scan_position += yyleng; ... }

由于将scan_position += yyleng;插入到每个规则中有点繁琐,您可以使用flex的有用YY_USER_ACTION宏挂钩:在每个操作的开头(甚至是空操作)扩展此宏。所以你可以更简单地写上面的内容:

%{
  long scan_position = 0;
  #define YY_USER_ACTION scan_position += yyleng;
%}

%%
[[:space:]]*      
"some pattern"    { ... }

一个警告:如果您使用任何调整令牌长度或以其他方式改变正常扫描过程的弹性操作,则此操作无效。其中至少包括yylessyymoreREJECTunputinput。如果您使用前三个中的任何一个,则需要重置scan_position -= yyleng;(这需要在调用yylessyymoreREJECT之前进行。对于{{1 }}和input,您需要递增/递减unput以考虑扫描过程之外的字符读取。

声明:

跟踪这样的位置假定从输入流读取的字节与底层文件系统中的原始字节之间存在一对一的对应关系。对于Posix系统,保证是这种情况:scan_positionfread(3)将读取相同的字节,read(2)开放模式标志无效。

但是,一般情况下,没有可靠的方法来跟踪文件位置。您可以在二进制模式下打开流并自己处理系统的特殊行结束(这将在Windows上运行,但没有可移植的方式来确定行结束序列,因此它也不可移植)。但是在其他非Posix系统上,二进制读取可能产生完全不同的结果(例如,底层文件可能使用固定长度的记录,以便每行填充(具有一些特定于系统的填充字符)这是正确的长度。

这就是为什么C标准禁止使用计算的b值:

  

对于文本流,offset应为零,或offset应为先前成功调用与同一文件关联的流上的offset函数返回的值从ftell开始。SEEK_SET。 (§7.21.9.2“fseek功能”,第4段)。

没有办法在flex中调用缓冲 - 或者我知道的任何版本的lex - 因为正确处理回退取决于能否缓冲。 (当扫描超出令牌的末尾时发生回退,因为令牌匹配较长令牌的前缀,该令牌恰好不存在。)

我认为唯一可移植的解决方案是将输入流令牌按令牌复制到您自己的缓冲区(或临时文件)中,然后使用yy_push_buffer_stateyy_scan_buffer(如果您使用的是缓冲区) )将该缓冲区插入输入流。该解决方案看起来很像上面的跟踪代码,除了YY_USER_ACTION将令牌读取附加到您自己的字符串缓冲区或临时文件。 (您可能希望在标志上设置该条件,以便它只发生在您要重新扫描的文件的片段中。)如果您有嵌套重复,您可以在自己的缓冲区/文件中跟踪位置,以便能够回到它。