1

我正在尝试编写一个 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 语法的心智模型是有缺陷的。)

4

2 回答 2

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”,但随后在贪婪的“*”前瞻中仅回溯一步,确定它有一个“变量”,然后重新解析“。” 像平常一样”。(请注意,这会拾取尾随_,如果不需要,可以通过 hack in action 或类似的东西将其剪掉;见下文。)

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

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

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

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]

现在,*当它无法向前窥视时,贪婪的群体将回溯_

于 2014-09-14T16:18:50.113 回答
0

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

于 2014-09-14T16:07:28.640 回答