稍微重新排列语法使其工作:
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]
现在,*
当它无法向前窥视时,贪婪的群体将回溯_
。