捕获含糊不清的文本片段的最佳方法是什么?

What is the best way to capture ambiguous segments of text?

在以下情况下,捕获内部文本的最佳方式是什么?

inner_text = any*;
tag_cdata = '<![CDATA[' inner_text >cdata_start %cdata_end ']]>';

问题是,由于 inner_text 可以匹配 ].

,因此 cdata_end 操作似乎触发了几次

我找到了解决方案。您需要处理非确定性。最初并不清楚,但正确的解决方案是这样的:

inner_text = any*;
tag_cdata = '<![CDATA[' inner_text >text_begin %text_end ']]>' %cdata_end;

action text_begin {
    text_begin_at = p;
}

action text_end {
    text_end_at = p;
}

action cdata_end {
    delegate.cdata(data.byteslice(text_begin_at, text_end_at-text_begin_at))
}

本质上,您要等到您确定已解析完整的 CDATA 标记,然后再使用您之前捕获的信息触发回调。

此外,我发现 Ragel 中的某些形式的不确定性需要使用优先级来明确处理。虽然这看起来有点难看,但在某些情况下这是唯一的解决方案。

在处理 (a+ >a_begin %a_end | b)* 这样的模式时,您会发现事件会针对遇到的每个 a 调用,而不是针对最长的子序列调用。在某些情况下,这种歧义可以使用最长匹配 kleene 星 ** 来解决。它的作用是更喜欢匹配现有模式而不是环绕。

令我惊讶的是,这实际上也修改了调用事件的方式。例如,这会产生一台在调用回调时无法一次缓冲多个字符的机器:

%%{
  machine example;

  action a_begin {}
  action a_end {}

  main := ('a'+ >a_begin %a_end | 'b')*;
}%%

产生:

您会注意到它每次都会调用 a_begina_end

相比之下,我们可以使内部循环和事件处理变得贪婪:

%%{
  machine example;

  action a_begin {}
  action a_end {}

  main := ('a'+ >a_begin %a_end | 'b')**;
}%%

产生: