1

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

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

问题是,由于 inner_text 可以 match ,该动作似乎cdata_end触发了多次]

4

1 回答 1

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 标记后再触发回调,使用之前捕获的信息。

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

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

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

%%{
  machine example;

  action a_begin {}
  action a_end {}

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

产生:

非贪心解析器

你会注意到它a_begin每次a_end都会调用。

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

%%{
  machine example;

  action a_begin {}
  action a_end {}

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

产生:

贪心解析器

于 2016-11-28T01:52:15.637 回答