检测_vars_with_underscores_;为什么这不起作用?

时间:2014-09-14 15:35:47

标签: javascript parsing grammar pegjs

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

2 个答案:

答案 0 :(得分: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()调用的动作代码来跟踪解析器的行为。

编辑 - 我认为这笔交易就是这样。在原始版本中,解析失败的形式为

的规则
  

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]

现在贪婪的*小组会在_向前看时失败。

答案 1 :(得分:0)

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