我正在尝试扩展PEG.js的示例语法,用于解析我的在线BASIC解释器实验的所有4个运算符的数学表达式:
http://www.dantonag.it/basicjs/basicjs.html
但并非所有表达式都被正确解析。
这是我的PEG语法:
expression = additive
additive = left:multiplicative atag:("+" / "-") right:additive { return {tag: atag, left:left, right:right}; } / multiplicative
multiplicative = left:primary atag:("*" / "/") right:multiplicative { return {tag: atag, left:left, right:right}; } / primary
primary = number / "(" additive:additive ")" { return additive; }
number = digits:[0-9]+ { return parseInt(digits.join(""), 10); }
它正确地解析了2 * 3 + 1(给出7)这样的表达式,但没有像2-1-1这样的表达式,它给出了2而不是0。
你能帮我改进和调试吗?
提前致谢。
编辑:我已将“数字”规则添加到语法中。是的,我的语法为输出提供了一个类似于解析树的递归结构。
答案 0 :(得分:8)
马特的回答是正确的,但关于如何在pegjs中实现左联结:
expression = additive
additive
= first:multiplicative rest:(("+" / "-") multiplicative)+ {
return rest.reduce(function(memo, curr) {
return {atag: curr[0], left: memo, right: curr[1]};
}, first);
}
/ multiplicative
multiplicative
= first:primary rest:(("*" / "/") primary)+ {
return rest.reduce(function(memo, curr) {
return {atag: curr[0], left: memo, right: curr[1]};
}, first);
}
/ primary
primary
= number
/ "(" additive:additive ")" { return additive; }
number
= digits:[0-9]+ { return parseInt(digits.join(""), 10); }
javascript.pegjs示例Matt链接使用类似的方法。关键是处理与列表具有相同优先级的操作字符串,这允许您使用正确的关联性构建树。
答案 1 :(得分:5)
首先:你的语法缺少number
规则。另外,正如我确定你知道的那样,在你的例子中运行你的语法(在添加number
之后)并没有给出2,而是像解析树一样。您是否介意更新问题以解决这两个问题?
<强>问题:强>
看起来你已经陷入了联想。当两个具有相同优先级的运算符竞争操作数时,关联性发挥作用。在您的示例中,-
与-
竞争 - 显然它将具有与其本身相同的优先级 - 但关联性对于打破+
和{{1}之间的联系也很重要} {},以及-
和*
之间。
我假设/
被正确解析,因为两个运算符具有不同的优先级,这意味着关联性不起作用,并且您的语法正确地实现了优先级(尽管您应该注意2*3+1
是一个更标准的例子,表明乘法的优先级高于加法,因为2+3*1
的简单从左到右解析得到与解析器相同的结果。
我假设您希望2*3+1
是左关联的,但基于此示例,它在您的语法中似乎是右关联的:
输入:
-
输出(解析为1-2-3
):
1-(2-3)
左关联树看起来像这样(来自{
"tag": "-",
"left": "1",
"right": {
"tag": "-",
"left": "2",
"right": "3"
}
}
):
(1-2)-3
您应该注意,您的其他运算符也似乎是右关联而不是左 - 。
解决方案:我真的不知道peg.js是如何运作的,但是有一些快速的谷歌搜索出现this。
基于语法的运算符优先级和关联性解决方案通常非常讨厌(请参阅grammar for Python获取证据),因此您可能需要查看[自上而下]运算符优先级解析以获得更灵活和更具表现力的替代方案。 Douglas Crockford,Vaughn Pratt和Annika Aasa都有一些关于这个主题的好文章。