这适用于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"在语法中?
答案 0 :(得分:2)
Javascript本身(官方ECMAScript,由ECMA-262定义)使用BNF的扩展进行描述,该扩展允许使用布尔限定符(标准语言中的“参数”)来扩充规则。这正是您正在寻找的效果,它清楚地简化了语言有些错综复杂的语法的表达。有关BNF扩展的完整说明可以在标准的section 5.1.5 (Grammar Notation)中找到;总之,参数可以从左侧传递到右侧的非终端,也可以为RHS终端明确设置或取消设置;此外,它们可用于根据参数的存在与否来过滤可能的产品。 (这篇文章末尾有一个例子。)
这个特殊的BNF扩展不会给BNF增加任何生成能力;通过简单列举可能性,可以机械地消除它的所有用途。遗憾的是,我知道没有实现这种形式的语法生成器(尽管某些Javascript实现肯定可能包含自定义解析器生成器)。
出于您的目的,可以很容易地预处理您的jison语法以实现非常相似的东西。实际上,对bison语法文件进行预处理会相对容易,但使用jison会更容易,因为您可以通过编程方式计算语法并将其作为JSON对象传递给jison。此功能没有详细记录,但jison手册包含足够的示例,应该可以直接使用。例如,请参阅CommonJS section。
正如所承诺的,这里是ECMA-262语法的摘录,显示了这个BNF扩展的使用:
IdentifierReference
可以使用两个可能的布尔限定符(Yield
和Await
)进行限定,从而产生四种可能性。它总是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]
最后,一个带有显式参数的例子。在GeneratorMethod
的制作中,为Yield
制作明确指定了PropertyName
(这可以防止yield
被识别为参数列表中的标识符)和{{1 }}被定义为GeneratorBody
FunctionBody
(允许Yield
表达式并禁止yield
作为标识符)且没有yield
(不允许Await
}表达式,但允许await
成为标识符。
await
坚持向后兼容性需要上述复杂性中的大部分:因为为早期JS版本编写的程序可能使用GeneratorMethod[Yield, Await]:
* PropertyName[?Yield, ?Await] ( UniqueFormalParameters[+Yield, ~Await] ) { GeneratorBody }
GeneratorBody:
FunctionBody[+Yield, ~Await]
和yield
作为变量名,因此这些关键字仅保留在语法中早期版本中没有的上下文。 (这是过于简单化,但细节远远超出了这个问题的范围。)