Pyparcing优先级与Unary运算符断开



我正在尝试使用pyparsing实现用于算术解析的Python运算符的子集。我有以下代码来实现我的解析器:

variable_names = pyparsing.Combine(pyparsing.Literal('$') + pyparsing.Word(pyparsing.alphanums + '_'))
integer = pyparsing.Word(pyparsing.nums)
double = pyparsing.Combine(pyparsing.Word(pyparsing.nums) + '.' + pyparsing.Word(pyparsing.nums))
parser = pyparsing.operatorPrecedence(variable_names | double | integer, [
('**', 2, pyparsing.opAssoc.RIGHT),
('-', 1, pyparsing.opAssoc.RIGHT),
(pyparsing.oneOf('* / // %'), 2, pyparsing.opAssoc.LEFT),
(pyparsing.oneOf('+ -'), 2, pyparsing.opAssoc.LEFT),
(pyparsing.oneOf('> >= < <= == !='), 2, pyparsing.opAssoc.LEFT),
('not', 1, pyparsing.opAssoc.RIGHT),
('and', 2, pyparsing.opAssoc.LEFT),
('or', 2, pyparsing.opAssoc.LEFT)])

在大多数情况下,这很好,尽管有时当我使用一元-时会中断。具体地说,我认为(我可能错了(如果我在更高优先级的操作数之后使用-,它就会中断,在这种情况下,它只是**。以下示例显示了该问题:

parsing 5 * 10 * -2             yields: ['5', '*', '10', '*', ['-', '2']]
parsing 5 * 10 ** -2            yields: ['5', '*', '10']               # Wrong
parsing 5 * 10 ** (-2)          yields: ['5', '*', ['10', '**', ['-', '2']]]
parsing 5 and not 8             yields: ['5', 'and', ['not', '8']]
parsing 5 and - 8               yields: ['5', 'and', ['-', '8']]

发生这种情况有什么原因吗?我错过了什么?

对我来说,你应该将-定义为高于**

('-', 1, pyparsing.opAssoc.RIGHT),
('**', 2, pyparsing.opAssoc.RIGHT),

这应该能解决你的问题。


最小工作代码

import pyparsing
variable_names = pyparsing.Combine(pyparsing.Literal('$') + pyparsing.Word(pyparsing.alphanums + '_'))
integer = pyparsing.Word(pyparsing.nums)
double = pyparsing.Combine(pyparsing.Word(pyparsing.nums) + '.' + pyparsing.Word(pyparsing.nums))
parser = pyparsing.operatorPrecedence(
variable_names | double | integer,
[
('-',  1, pyparsing.opAssoc.RIGHT),
('**', 2, pyparsing.opAssoc.RIGHT),
(pyparsing.oneOf('* / // %'), 2, pyparsing.opAssoc.LEFT),
(pyparsing.oneOf('+ -'), 2, pyparsing.opAssoc.LEFT),
(pyparsing.oneOf('> >= < <= == !='), 2, pyparsing.opAssoc.LEFT),
('not', 1, pyparsing.opAssoc.RIGHT),
('and', 2, pyparsing.opAssoc.LEFT),
('or',  2, pyparsing.opAssoc.LEFT)
]
)
examples = [
"5 * 10 ** -2",
"5 * 10 * -2",
"5 * 10 ** (-2)",
"5 * -10 ** 2",
"5 * (-10) ** 2",    
"5 and not 8",
"5 and -8",
"1 ** -2",
"-1 ** 2",
]
longest = max(map(len, examples))
for ex in examples:
result = parser.parseString(ex)
print(f'{ex:{longest}}  <=>  {result}')

结果:

5 * 10 ** -2    <=>  [['5', '*', ['10', '**', ['-', '2']]]]
5 * 10 * -2     <=>  [['5', '*', '10', '*', ['-', '2']]]
5 * 10 ** (-2)  <=>  [['5', '*', ['10', '**', ['-', '2']]]]
5 * -10 ** 2    <=>  [['5', '*', [['-', '10'], '**', '2']]]
5 * (-10) ** 2  <=>  [['5', '*', [['-', '10'], '**', '2']]]
5 and not 8     <=>  [['5', 'and', ['not', '8']]]
5 and -8        <=>  [['5', 'and', ['-', '8']]]
1 ** -2         <=>  [['1', '**', ['-', '2']]]
-1 ** 2         <=>  [[['-', '1'], '**', '2']]

BTW:用于比较:C运算符优先级和Python-运算符优先级


编辑:

当我在-之前保留**但我使用时,我可以为5 * -10 ** 2([[5, '*', ['-', [10, '**', 2]]]](获得-500

integer = pyparsing.pyparsing_common.signed_integer

import pyparsing
variable_names = pyparsing.Combine(pyparsing.Literal('$') + pyparsing.Word(pyparsing.alphanums + '_'))
#integer = pyparsing.Word(pyparsing.nums)
integer = pyparsing.pyparsing_common.signed_integer
double = pyparsing.Combine(pyparsing.Word(pyparsing.nums) + '.' + pyparsing.Word(pyparsing.nums))
parser = pyparsing.operatorPrecedence(
variable_names | double | integer,
[
('**', 2, pyparsing.opAssoc.RIGHT),
('-',  1, pyparsing.opAssoc.RIGHT),
(pyparsing.oneOf('* / // %'), 2, pyparsing.opAssoc.LEFT),
(pyparsing.oneOf('+ -'), 2, pyparsing.opAssoc.LEFT),
(pyparsing.oneOf('> >= < <= == !='), 2, pyparsing.opAssoc.LEFT),
('not', 1, pyparsing.opAssoc.RIGHT),
('and', 2, pyparsing.opAssoc.LEFT),
('or',  2, pyparsing.opAssoc.LEFT)
]
)
examples = [
"5 * 10 ** -2",
"5 * 10 * -2",
"5 * 10 ** (-2)",
"5 * -10 ** 2",
"5 * (-10) ** 2",    
"5 and not 8",
"5 and -8",
"1 ** -2",
"-1 ** 2",
]
longest = max(map(len, examples))
for ex in examples:
result = parser.parseString(ex)
print(f'{ex:{longest}}  <=>  {result}')

结果:

5 * 10 ** -2    <=>  [[5, '*', [10, '**', -2]]]
5 * 10 * -2     <=>  [[5, '*', 10, '*', ['-', 2]]]
5 * 10 ** (-2)  <=>  [[5, '*', [10, '**', ['-', 2]]]]
5 * -10 ** 2    <=>  [[5, '*', ['-', [10, '**', 2]]]]
5 * (-10) ** 2  <=>  [[5, '*', [['-', 10], '**', 2]]]
5 and not 8     <=>  [[5, 'and', ['not', 8]]]
5 and -8        <=>  [[5, 'and', ['-', 8]]]
1 ** -2         <=>  [[1, '**', -2]]
-1 ** 2         <=>  [['-', [1, '**', 2]]]

pyparsing_common的文档,带有其他预定义表达式

很抱歉带回这么旧的主题,但我正在为我的项目编写一个非常相似的解析器,它混合了布尔逻辑和数学运算符,我最终得到了一个非常类似的代码,看起来很合适。

由于这里给出的解析器遇到了与我相同的问题,我将使用它来展示我的问题。

我不能让它解析"$true==not$false";,基本上;而不是";比较后没有括号是行不通的。

$true == $false        <=>  [['$true', '==', '$false']]
$true == not $false    <=>  ['$true']
$true == (not $false)  <=>  [['$true', '==', ['not', '$false']]]

正如你在没有";而不是";或者用括号,它解析得很好,但用一个简单的";而不是";它似乎忽略了"==">

移动";而不是";中缀表示法中的第一个运算符(以前的运算符优先级(。然后我得到了这些结果:

$true == not $false    <=>  [['$true', '==', ['not', '$false']]]
$true == (not $false)  <=>  [['$true', '==', ['not', '$false']]]

这是伟大的,但它当然打破了运算符的优先级,比如"$真而不是10==9〃;我想被解析"$真与否(10==9(";像在python中一样,而是像这样解析:

$true and not 10 == 9  <=>  [['$true', 'and', [['not', 10], '==', 9]]]

我想知道您是否在解析器的实现中遇到了这些用例,并找到了解决它们的方法。

最新更新