(我使用Yecc,一个类似于Yacc的Erlang解析器生成器,所以语法与Yacc不同)
问题很简单,比方说我们要解析一个lispy语法,我希望匹配表达式列表。 表达式列表是用空格分隔的表达式列表。
在Erlang中,[1,3,4]
是一个列表,++
连接两个列表。
我们想要匹配此1 (1+2) 3
。 expression
将匹配1,(1 + 2)和3.因此,我在列表上匹配后跟一个表达式匹配,如果没有匹配,我将在单个表达式上匹配。这会递归地构建一个列表。
expressionlist -> expressionlist expression : '$1' ++ ['$2'].
expressionlist -> expression : ['$1'].
但我也可以这样做(颠倒顺序):
expressionlist -> expression expressionlist : ['$1'] ++ '$2'.
expressionlist -> expression : ['$1'].
两个人似乎都有效,我想知道是否有任何不同。
使用分隔符
我想匹配{name = albert , age = 43}
。 propdef
匹配name = value
。所以这是同样的问题,但有一个额外的分隔符,
。第一个问题与那里有什么不同吗?
proplist -> propdef ',' proplist : ['$1'] ++ '$3'.
proplist -> propdef : ['$1'].
proplist -> '{' proplist '}' : '$2'.
proplist -> '{' '}' : [].
%% Could write this
%% proplist -> proplist ',' propdef : '$1' ++ ['$3'].
谢谢。
答案 0 :(得分:2)
由于Yecc是一个LALR解析器生成器,因此使用左递归或右递归并不重要。在过去,人们更喜欢Yacc / Bison和类似工具中的左递归,因为它允许解析器继续减少而不是将所有内容都移到堆栈上直到列表的末尾,但是现在,堆栈空间和速度不是那么很重要,所以你可以挑选最适合自己的东西。 (请注意,在LL解析器中,左递归会导致无限循环,因此对于此类解析器,必须进行正确的递归。)
更重要的是,对于上面的示例,左递归版本中的'$ 1'++ ['$ 2']将导致二次时间复杂度,因为“表达式列表”部分将会更长更长的使用++运算符时,左边不应该有增长的组件。如果您解析数千个元素的列表,这种复杂性会伤害您。使用正确的递归版本,['$ 1'] ++'$ 2'将为您提供线性时间,即使解析器必须在开始减少之前将整个列表移动到堆栈上。您可以尝试两个版本并解析一个非常长的列表以查看差异。
在“propdef”中使用分隔符,“proplist”不会改变问题。