首页 > 解决方案 > 将 EBNF 语法转换为 pyparsing 会出错

问题描述

我正在制作一个解析器来将一个简单的 DSL 转换为elasticsearch查询。一些可能的查询是:

response:success
response:success AND extension:php OR extension:css
response:sucess AND (extension:php OR extension:css)
time >= 2020-01-09
time >= 2020-01-09 AND response:success OR os:windows
NOT reponse:success
response:success AND NOT os:windows

EBNF为此编写了以下语法:

<expr> ::= <or>
<or> ::= <and> (" OR " <and>)*
<and> ::= <unary> ((" AND ") <unary>)*
<unary> ::= " NOT " <unary> | <equality>
<equality> ::=  (<word> ":" <word>) | <comparison>
<comparison> ::= "(" <expr> ")" | (<word> (" > " | " >= " | " < " | " <= ") <word>)+
<word> ::= ("a" | "b" | "c" | "d" | "e" | "f" | "g"
                      | "h" | "i" | "j" | "k" | "l" | "m" | "n"
                      | "o" | "p" | "q" | "r" | "s" | "t" | "u"
                      | "v" | "w" | "x" | "y" | "z")+

DSL 中运算符的优先级是: () > NOT > AND > OR 同样精确的数学运算,即':' 具有比比较运算符更高的优先级。

我相信上面的语法抓住了我的 DSL 的想法。我很难将它翻译成 pyparsing,这就是我现在所拥有的:

from pyparsing import *

AND = Keyword('AND') | Keyword('and')
OR  = Keyword('OR')  | Keyword('or')
NOT = Keyword('NOT') | Keyword('not')

word = Word(printables, excludeChars=':')

expr = Forward()

expr << Or
Comparison = Literal('(') + expr + Literal(')')  + OneOrMore(word + ( Literal('>') | Literal('>=') | Literal('<') | Literal('<=')) + word)
Equality = (word + Literal(':') + word) | Comparison
Unary = Forward()
Unary << (NOT + Unary) | Equality
And = Unary + ZeroOrMore(AND + Unary)
Or = And + ZeroOrMore(OR + And)

我得到的错误是:

Traceback (most recent call last):
  File "qql.py", line 54, in <module>
    expr << Or
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/pyparsing.py", line 5006, in __lshift__
    self.mayIndexError = self.expr.mayIndexError
AttributeError: type object 'Or' has no attribute 'mayIndexError'

我认为这是因为我无法Forward()正确理解。

问题我怎样才能正确地将上述语法翻译成pyparsing?

**编辑**:当我将pyparsing代码更改为

AND = Keyword('AND')
OR  = Keyword('OR')
NOT = Keyword('NOT')
word = Word(printables, excludeChars=':')

expr = Forward()
Comparison = Literal('(') + expr + Literal(')')  + OneOrMore(word + ( Literal('>') | Literal('>=') | Literal('<') | Literal('<=')) + word)
Equality = (word + Literal(':') + word) | Comparison
Unary = Forward()
Unary << ((NOT + Unary) | Equality)
And = Unary + ZeroOrMore(AND) + Unary
Or = And + ZeroOrMore(OR + And)
expr << Or


Q = """response : 200        \
    AND extesnion: php       \
    OR  extension: css       \
    """
print(expr.parseString(Q))

我得到这个输出:

['response', ':', '200', 'AND', 'extesnion', ':', 'php']

为什么不解析 OR 表达式?

标签: pythonpython-3.xpyparsing

解决方案


推荐阅读