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

时间:2016-11-19 01:06:00

标签: parsing regular-language ragel

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

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

问题是,由于inner_text可以与cdata_end匹配,似乎]动作会多次触发。

1 个答案:

答案 0 :(得分:2)

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

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标记。<​​/ p>

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

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

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

%%{
  machine example;

  action a_begin {}
  action a_end {}

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

产地:

Non-greedy Parser

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

相反,我们可以让内循环和事件处理变得贪婪:

%%{
  machine example;

  action a_begin {}
  action a_end {}

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

产生:

Greedy Parser