2017-06-29 29 views
0

这是Jison中的解析器,但我猜这同样适用于Bison。在递归规则中组合类似的构造

我有一个规则有一个表达式的定义。

expr 
    : NUMBER -> { type: "number", value: $1 } 
    | "(" expr ")" -> $2 
    | expr "+" expr -> { type: "+", left: $1, right: $3 } 
    | expr "-" expr -> { type: "-", left: $1, right: $3 } 
    | expr "*" expr -> { type: "*", left: $1, right: $3 } 
    | expr "/" expr -> { type: "/", left: $1, right: $3 } 
    ; 

我同样的语法我也有一个“过滤表达式”,也支持“参数”的规则。

filterExpr 
    : NUMBER -> { type: "number", value: $1 } 
    | PARAM -> { type: "param", name: $1 } /* parameter */ 
    | "(" filterExpr ")" -> $2 
    | filterExpr "+" filterExpr -> { type: "+", left: $1, right: $3 } 
    | filterExpr "-" filterExpr -> { type: "-", left: $1, right: $3 } 
    | filterExpr "*" filterExpr -> { type: "*", left: $1, right: $3 } 
    | filterExpr "/" filterExpr -> { type: "/", left: $1, right: $3 } 
    ; 

这有效,但是当我添加运算符时,我必须更改这两个定义。有没有办法在语法中结合“expr”和“filterExpr”的共同部分?

回答

2

使用BNF的扩展来描述Javascript本身(正式ECMAScript,由ECMA-262定义),它允许使用布尔限定符(标准语言中的“参数”)来增加规则。这正是你正在寻找的效果,它显然简化了语言的复杂语法的表达。标准的section 5.1.5 (Grammar Notation)中可以找到BNF扩展的完整解释;总之,参数可以从左侧传递到右侧的非终端,或者可以为RHS终端明确设置或取消设置;此外,它们可以用于根据参数的存在与否来过滤可能的产品。 (这篇文章末尾有一个例子。)

这个特殊的BNF扩展不会增加BNF的生成能力;通过简单列举可能性,可以机械地消除它的所有用途。可悲的是,我知道没有实现这种形式的语法生成器(尽管某些Javascript实现当然可能包含一个自定义的解析器生成器)。

出于您的目的,预处理您的jison语法以实现非常相似的内容很容易。事实上,预处理一个野牛语法文件相对容易,但jison更容易,因为您可以通过编程来计算语法并将其作为JSON对象传递给jison。这个功能没有很好的记录,但是jison手册包含了足够的例子,它应该是直接使用的。例如,请参阅CommonJS section


正如所承诺的,这里是从ECMA-262的语法的摘录,显示使用该BNF扩展:

IdentifierReference可以有两个可能的布尔预选赛(YieldAwait)引起四个合格可能性。它始终可以是Identifier;只有在不符合Yield属性的限定条件时才可以使用关键字yield,仅在不符合Await的条件下使用关键字await

IdentifierReference[Yield, Await]: 
    Identifier 
    [~Yield]yield 
    [~Await]await 

所以这单节就相当于四个非终端,它可以机械制造:

IdentifierReference: Identfier | yield | await 
IdentifierReference_Yield: Identifier | await 
IdentifierReference_Await: Identifier | yield 
IdentifierReference_Yield_Await: Identifier 

下面是它的应用方式:一个Expression可以用三个属性合格,所有这一切通过(,?Yield)到右侧的非终端。

Expression[In, Yield, Await]: 
    AssignmentExpression[?In, ?Yield, ?Await] 
    Expression[?In, ?Yield, ?Await], AssignmentExpression[?In, ?Yield, ?Await] 

yield表达只允许在AssignmentExpression合格的变体与Yield

AssignmentExpression[In, Yield, Await]: 
    ConditionalExpression[?In, ?Yield, ?Await] 
    [+Yield]YieldExpression[?In, ?Await] 

最后,具有明确的参数的示例。在生产用于GeneratorMethodYield被明确为PropertyName生产(其防止yield被识别为在参数列表中的标识符)指定和GeneratorBody被定义为FunctionBodyYield(允许yield表达式和禁止yield作为标识符)并且没有Await(不允许await表达式,但允许await作为标识符)。

GeneratorMethod[Yield, Await]: 
    * PropertyName[?Yield, ?Await]( UniqueFormalParameters[+Yield, ~Await]){ GeneratorBody } 

GeneratorBody: 
    FunctionBody[+Yield, ~Await] 

大部分上述复杂的是通过向后兼容的坚持要求:因为对于早期JS版本编写的程序可能使用yieldawait作为变量名,这些关键字在没有语法语境只保留在早期版本中可用。 (这是一个过于简单化,但细节已经超出了这个问题的范围。)

+0

这么长的故事,简单的Jison是不可能的,并且需要定制的语法预处理? – rveerd

+0

@ rveerd:是的,就是这样。但对于野牛来说,自定义预处理可能很简单,使其值得。 – rici