【发布时间】:2021-10-21 17:02:00
【问题描述】:
我正在尝试为Microsoft article 中描述的 ebnf 语法生成一个 javascript 解析器。文章中指定的 ebnf 在我使用它时不起作用,因此我尝试对其进行简化以使其与 REx 解析器生成器一起使用。
目标是在 Javascript 中能够解析和评估这样的表达式为 True 或 False:
-
AttributeA > 2- AttributeA的值大于2 -
HasCategory(Assembly)- 节点有分类组装 -
Assembly- 节点有分类组装 -
HasValue(AttributeA)- 属性 AttributeA 有一个值。它不是未定义的。 -
AttributeA < AttributeB- 属性 AttributeA 的值小于属性 Attribute B 的值 -
IsReference- 属性 IsReference 的值为 True AttributeA + 2 > 5 and AttributeB - 5 != 7AttributeA * 1.25 >= 500
我在这里在线使用 REx 解析器生成器:https://bottlecaps.de/rex/。如果您对生成 JavaScript 的其他解析器生成器有任何建议,我将不胜感激一些指向我可以找到它们的链接。
我正在努力解决的问题是 MethodCall 的定义。我尝试了很多不同的定义,但都失败了。当我删除 MethodCall 和 MethodArgs 定义时,REx 解析器生成器会生成一个解析器。
因此,如果您能帮我解决这个问题,我将不胜感激。
以下是我所能得到的语法。
Expression
::= BinaryExpression | MethodCall | "(" Expression ")" | Number
BinaryExpression
::= RelationalExpression ( ( '=' | '!=' ) RelationalExpression )*
RelationalExpression
::= AdditiveExpression ( ( '<' | '>' | '<=' | '>=' | 'and' | 'or' ) AdditiveExpression )*
AdditiveExpression
::= MultiplicativeExpression ( ( '+' | '-' ) MultiplicativeExpression )*
MultiplicativeExpression
::= UnaryExpression ( ( '*' | '/' | '%' ) UnaryExpression )*
UnaryExpression
::= "!" Identifier | "+" Identifier | "-" Identifier | Identifier
MethodCall
::= Identifier "(" MethodArgs* ")"
MethodArgs
::= Expression | Expression "," MethodArgs
Identifier
::= Letter ( Letter | Digit | "_" )*
Number
::= Digit ('.' Digit) | (Digit)*
<?TOKENS?>
Letter
::= [A-Za-z]
Digit
::= [0-9]
以下是我尝试过的 MethodCall 定义的一些不同版本,但都失败了。
MethodCall
::= Identifier "(" MethodArgs? ")"
MethodArgs
::= Expression ("," MethodArgs)*
MethodCall
::= Identifier "(" MethodArgs ")"
MethodArgs
::= ( Expression ("," MethodArgs)* )?
MethodCall
::= MethodName "(" MethodArgs? ")"
MethodName
::= Identifier
MethodArgs
::= Expression ("," MethodArgs)*
MethodCall
::= Identifier "(" MethodArgs ")"
MethodArgs
::= Expression ("," MethodArgs)* | ""
MethodCall
::= Identifier MethodArgs
MethodArgs
::= "(" (Expression ("," MethodArgs)* | "") ")"
MethodCall
::= Identifier "(" MethodArgs ")"
MethodArgs
::= Expression | Expression "," MethodArgs | ""
MethodCall
::= Identifier "(" Expression ")"
我试图从其他一些语言中获得灵感,看看它们是如何做到的,但到目前为止还没有运气:
- https://bottlecaps.de/rex/EcmaScript.ebnf
- https://bottlecaps.de/rex/Java.ebnf
- https://cs.au.dk/~amoeller/RegAut/JavaBNF.html
- https://docs.python.org/3/reference/compound_stmts.html#function-definitions
更新
只是想让您知道结果如何。我努力让 EBNF 语法做我需要做的事情,所以我像@rici 建议的那样查看了Nearley,并将我的语法转换为Nearley 语法并让工具工作。这对我的项目来说是一个更好的选择。文档非常好,工具也很棒,错误消息也很有帮助。非常感谢 @rici 建议 Nearley。
下面是我实现的语法。我已经使用以下输入进行了测试:
'2 + 4', '2 + 4 - 6', '(2 + 4)', '!true', '!(true)', 'hasCategory(test)', 'hasCategory(test,test2 )', 'hasCategory(test, test2)', 'hasCategory(test,test2, test3)', 'IsReference', 'IsReference()', '2 * 4', '(2 / 4)', '2 * 4 + 2'、'(2 / 4) + 2'、'2 > 4'、'2 >= 2'、'2 = 4'、'2 == 2'、'2 != 4'、'2 !== 2', '(2 * 4 + 2) > 4', '(2 * 4 + 2) > (4 + 10)', 'true', 'true or false', 'true || false', 'true and false', 'true && false', '(true or false)', '!(true or false)', '2 != 1+1', '2 != (1+1) '、'2 != (1+2)'、'(2 > 2 或 (2 != 1+1))'、
@builtin "whitespace.ne" # `_` means arbitrary amount of whitespace
@builtin "number.ne" # `int`, `decimal`, and `percentage` number primitives
@builtin "postprocessors.ne"
@{%
function methodCall(nameId, argsId = -1) {
return function(data) {
return {
type: 'methodCall',
name: data[nameId],
args: argsId == -1 ? [] : data[argsId]
};
}
}
function value() {
return function(data) {
return {
type: 'value',
value: data[0]
};
}
}
%}
expression ->
methodCall {% id %}
| relationalExpression {% value() %}
| booleanExpression {% value() %}
| _ identifier _ {% methodCall(1) %}
booleanExpression ->
parentheses {% id %}
| parentheses _ "and"i _ parentheses {% d => d[0] && d[4] %}
| parentheses _ "&&" _ parentheses {% d => d[0] && d[4] %}
| parentheses _ "or"i _ parentheses {% d => d[0] || d[4] %}
| parentheses _ "||" _ parentheses {% d => d[0] || d[4] %}
parentheses ->
_ "(" relationalExpression ")" _ {% nth(2) %}
| _ "(" booleanExpression ")" _ {% nth(2) %}
| unaryExpression {% id %}
relationalExpression ->
_ additiveExpression _ {% nth(1) %}
| relationalExpression _ "=" _ additiveExpression {% d => d[0] == d[4] %}
| relationalExpression _ "==" _ additiveExpression {% d => d[0] == d[4] %}
| relationalExpression _ "!=" _ additiveExpression {% d => d[0] != d[4] %}
| relationalExpression _ "!==" _ additiveExpression {% d => d[0] != d[4] %}
| relationalExpression _ "<" _ additiveExpression {% d => d[0] < d[4] %}
| relationalExpression _ ">" _ additiveExpression {% d => d[0] > d[4] %}
| relationalExpression _ "<=" _ additiveExpression {% d => d[0] <= d[4] %}
| relationalExpression _ ">=" _ additiveExpression {% d => d[0] >= d[4] %}
additiveExpression ->
_ multiplicativeExpression _ {% nth(1) %}
| additiveExpression _ "+" _ multiplicativeExpression {% d => d[0] + d[4] %}
| additiveExpression _ "-" _ multiplicativeExpression {% d => d[0] - d[4] %}
multiplicativeExpression ->
_ parentheses _ {% nth(1) %}
| parentheses _ "*" _ parentheses {% d => d[0] * d[4] %}
| parentheses _ "/" _ parentheses {% d => d[0] / d[4] %}
| parentheses _ "%" _ parentheses {% d => d[0] % d[4] %}
unaryExpression ->
_ "!" _ expression _ {% d => !d[3] %}
| _ decimal _ {% nth(1) %}
| _ unsigned_int _ {% nth(1) %}
| _ boolean _ {% nth(1) %}
| _ identifier _ {% nth(1) %}
methodCall ->
identifier "(" methodArgs ")" {% methodCall(0, 2) %}
| identifier "(" _ ")" {% methodCall(0) %}
methodArgs ->
_ identifier _ {% d => [d[1]] %}
| _ identifier _ "," _ methodArgs _ {% d => [d[1]].concat(d[5]) %}
boolean ->
"true"i {% () => true %}
| "false"i {% () => false %}
identifier ->
[A-Za-z0-9_]:+ {% (data, l, reject) => {
var ident = data[0].join('');
if (ident.toLowerCase() === 'true' || ident.toLowerCase() === 'false') {
return reject;
} else {
return ident;
}
}
%}
【问题讨论】:
-
正确的定义是
MethodCall ::= Identifier "(" MethodArgs? ")"和MethodArgs ::= Expression ("," Expression)*。你所有的例子都不正确;大多数也是模棱两可的。请注意,您还必须更改Number以使其与空虚不匹配。我建议Number ::= Digit+ ("." Digit*)? | "." Digit+。但它需要与标识符一起位于令牌部分。您还需要定义一个空格规则;否则and和or将很难匹配。还有很多其他的错误,不是所有的都在 kaby76 的回答中。 -
您可能想查看nearly,它包含大量文档,包括教程。 (我用的不多,但我正在考虑从jison切换。)
标签: parsing grammar ebnf nearley