【问题标题】:Detecting _vars_with_underscores_; why does this not work?检测_vars_with_underscores_;为什么这不起作用?
【发布时间】:2014-09-15 00:40:45
【问题描述】:

我正在尝试编写一个 PEGjs 规则来转换

Return _a_b_c_.

Return <>a_b_c</>.

我的语法是

root = atoms:atom+
{ return atoms.join(''); }

atom = variable
     / normalText

variable = "_" first:variableSegment rest:$("_" variableSegment)* "_"
{ return '<>' + first + rest + '</>'; }

variableSegment = $[^\n_ ]+

normalText = $[^\n]

这适用于

Return _a_b_c_ .

Return _a_b_c_

但是出现了问题

Return _a_b_c_.

例子。

我不太明白为什么会出现这种情况,并且希望能解释一下它为什么会这样。 (我什至不需要解决这个问题,当然;最大的问题是我对 PEGjs 语法的心智模型有缺陷。)

【问题讨论】:

  • _. 匹配"_" variableSegment 部分,但缺少尾随"_",这就是使用normalText 规则的原因。 (Return _a_b_c_.Return _a_b_c_d相同
  • 为什么在这样解释时看不到规则不匹配,而是将最后一个 _ 解释为尾随 _?
  • 当它在. 之后没有看到尾随_ 时失败,它会通过假设前导 _a 是“正常”来回溯。我不确定我知道为什么。
  • 哦 - 如果您不希望尾随 _

标签: javascript parsing grammar pegjs


【解决方案1】:

稍微重新排列语法使其起作用:

root = atoms:atom+
{ return atoms.join(''); }

atom = variable
     / normalText

variable = "_" first:$(variableSegment "_") rest:$(variableSegment "_")*
{ return '<>' + first + rest + '</>'; }

variableSegment = seg:$[^\n_ ]+

normalText = normal:$[^\n]

我不确定我完全理解为什么。在这一个中,解析器到达“。”并将其匹配为“variableSegment”,但随后在贪婪的“*”前瞻中仅回溯一步,确定它有一个“变量”,然后重新解析“。”像平常一样”。 (请注意,这会拾取尾随的_,如果不需要,可以通过正在运行的黑客或类似的东西将其剪掉;见下文。)

在原始版本中,由于缺少尾随下划线而失败后,解析器的下一步就是回到 前导 下划线,选择“正常”解释。

我添加了一些带有 console.log() 调用的操作代码来跟踪解析器的行为。

edit — 我认为交易是这样的。在您的原始版本中,解析在以下形式的规则上失败

expr1 expr2 expr3 ... exprN

第一个子表达式是文字_。接下来是第一个变量段。第三个是前面有_ 的变量表达式序列,最后一个是后面的_。在对有问题的输入执行该规则时,最后一个表达式失败。然而,其他人都成功,所以唯一重新开始的地方是“原子”规则中的替代点。

在修改后的版本中,解析器可以将贪婪的*的操作一步步展开。然后它成功匹配第三个表达式,因此规则成功。

因此,另一个更接近原始版本的修订版也将起作用:

root = atoms:atom+
{ return atoms.join(''); }

atom = variable
     / normalText

variable = "_" first:variableSegment rest:$("_" variableSegment & "_")* "_"
{ return '<>' + first + rest + '</>'; }

variableSegment = $[^\n_ ]+

normalText = $[^\n]

现在,贪婪的* 组将在无法向前窥视_ 时回溯。

【讨论】:

  • 那是因为规则不再模糊了。否则它将找到另一个variableSegment "_" 或变量已完成(它是该规则的最后一部分)。 PEG.js 在variable 内部并没有倒退一步。它尝试逐步匹配它,无论是实现(在您的情况下)还是原始代码中的失败。 (抱歉解释不好)
  • @t.niese 是的,这就是我在刚刚编辑的部分中的意思。一旦其中一个子表达式匹配,它就不会在列表中回溯。我对 why 没有深入的了解,但我认为这只是元语法的语义。
  • 我认为它是为 PEG.js 定义的,以避免无法再确定为什么文本的某个部分将匹配语法的情况。解析器越灵活,调试规则就越困难。
  • @t.niese 是的,它不愿意积极地回溯这一点是有道理的,因为无论如何都可以在语法中更加明确。
  • 这非常有用。我在原件中犯了一个错误;输出应该是Return &lt;&gt;a_b_c&lt;/&gt;(没有尾随_)。我编辑了我的 OP,您可能想编辑您的答案以免混淆未来的人,但您对问题的剖析非常有帮助。
【解决方案2】:

解析器将最后一个 _. 解释为 variableSegment。如果您从 variableSegment RegExp 中排除点,您的代码将按预期工作。

【讨论】:

  • 我不认为“。”性格有什么特别的意义。
  • 请添加更多详细信息
  • variableSegment = $[^\n_\. ]+ 正如 fgnass 所说,不包括 .解决问题。您是否尝试过使用pegjs.majda.cz/online
  • 是的,但是Return _a_b_c_d 仍然无法按预期工作。
  • 据我了解@Domenic 的语法变量应始终以下划线结尾?
猜你喜欢
  • 1970-01-01
  • 2015-07-19
  • 2012-11-17
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 2011-10-13
相关资源
最近更新 更多