我正在尝试编写一个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语法的心智模型是不足的。)
答案 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中排除点,则代码将按预期工作。