解析Python表达式而不进行求值
我有一个用Electron/Typescript编写的应用程序,我需要验证用户输入是否是有效的Python表达式 例如:解析Python表达式而不进行求值,python,parsing,Python,Parsing,我有一个用Electron/Typescript编写的应用程序,我需要验证用户输入是否是有效的Python表达式 例如: cos(PARAMPOLY.engineeringValue1)+cos(PARAMPOLY.engineeringValue2) x+y+z 我无法为这些表达式生成正确的操作数类型和值。我需要一些东西来解析表达式并告诉我是否有表达式错误 Pythoneval()函数解析并计算表达式。我只需要一个语法分析 有什么需要吗?您可能希望将完整的编译成完整的Python代码对象,或者
cos(PARAMPOLY.engineeringValue1)+cos(PARAMPOLY.engineeringValue2)
x+y+z
eval()
函数解析并计算表达式。我只需要一个语法分析
有什么需要吗?您可能希望将完整的编译成完整的Python代码对象,或者您可以只解析成抽象语法树。您可以使用来实现,也可以仅使用来生成树
解析为AST将输入标记化,并输出语法对象树,然后您可以进一步分析或转换这些语法对象。编译成字节码更进一步,使用该AST创建一个Python代码对象,您可以选择使用eval()
或;请注意,后者总是返回None
,并且可能不是计算表达式代码对象的最佳选择
eval(string)
使用eval(compile(string,“,”eval”)
将字符串参数编译成代码对象,然后执行它,因此compile(string,“,”eval”)
将在不执行的情况下给出相同的结果
如果只有表达式有效,则使用“eval”
作为模式;如果要接受完整的Python语句,则使用“exec”
<如果输入不是有效的Python表达式(“eval”
)或无效语句(“exec”
),则code>compile()(和ast.parse()
)会引发语法错误
异常
演示:
请注意,上面忽略了上下文,因此也列出了
参数
属性名称。如果需要处理具有更多上下文的语法树,请编写一个语法树。非常感谢您的解释。更进一步地说,假设我期望在用户提供的表达式中包含像“cos”和“sin”这样的数学词汇项,我想检查它。例如:1-IN:cos(a)+sin(b)OUT:OK 2-IN:cos(a,b)OUT NOK 3-IN-fake(a)OUT NOK 4-IN-tan(a)OUT NOK当我理解时,解析是不够的,我需要使用eval表达式,但我不知道如何管理操作数从数学导入*print(eval('cos(a)+sin(b),{'cos':cos,'sin':sin}))
你有什么想法吗?@Julieng31:如果你需要计算数学表达式,那么看看其中一个答案指向哪里,这至少是你想做的一个起点。后者使用AST以安全和可控的方式驱动表达式执行。@Julieng31:无论如何,类似于cos(ab)
的内容将被解析为Call()
表达式,其中cos
作为可调用的名称,带有两个参数。然后,您可以使用前面存储的或信息之类的内容来查看该调用是否可以工作(len(inspect.signature(math.cos.parameters)
是1
,因此,不,该调用将失败)。
>>> example1 = "cos(PARAMPOLY.engineeringValue1) + cos(PARAMPOLY.engineeringValue2)"
>>> example2 = "x + y + z"
>>> compile(example1, "<stdin>", "eval")
<code object <module> at 0x111c2eae0, file "<stdin>", line 1>
>>> compile(example2, "<stdin>", "eval")
<code object <module> at 0x111c2e540, file "<stdin>", line 1>
>>> result2 = _
>>> eval(result2, {"x": 42, "y": 81, "z": 117})
240
>>> compile("not a valid expression", "<stdin>", "eval")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1
not a valid expression
^
SyntaxError: invalid syntax
>>> import ast
>>> tree1 = ast.parse(example1)
>>> tree2 = ast.parse(example2)
>>> ast.dump(tree2.body[0])
"Expr(value=BinOp(left=Call(func=Name(id='cos', ctx=Load()), args=[Attribute(value=Name(id='PARAMPOLY', ctx=Load()), attr='engineeringValue1', ctx=Load())], keywords=[]), op=Add(), right=Call(func=Name(id='cos', ctx=Load()), args=[Attribute(value=Name(id='PARAMPOLY', ctx=Load()), attr='engineeringValue2', ctx=Load())], keywords=[])))"
>>> ast.dump(tree2.body[0])
"Expr(value=BinOp(left=BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load())), op=Add(), right=Name(id='z', ctx=Load())))"
>>> {node.id for node in ast.walk(tree1) if isinstance(node, ast.Name)}
{'cos', 'PARAMPOLY'}
>>> {node.id for node in ast.walk(tree2) if isinstance(node, ast.Name)}
{'x', 'z', 'y'}