簡體   English   中英

解析Python表達式而不求值

[英]Parsing a Python expression without evaluation

我有一個用Electron / Typescript編寫的應用程序,我需要驗證用戶輸入是否為有效的Python表達式。

例如:

  1. cos(PARAMPOLY.engineeringValue1) + cos(PARAMPOLY.engineeringValue2)
  2. x + y + z

我無法為這些表達式生成正確的操作數類型和值。 我需要解析表達式的內容,並告訴我是否存在表達式錯誤。

Python eval()函數解析並評估表達式。 我只需要一個解析。

有什么需要的嗎?

您可能希望將其完全編譯為一個完整的Python代碼對象,或者可以將其解析為一個抽象語法樹。 您可以使用compile()函數來實現,也可以僅使用ast.parse()來生成樹。

解析為AST會標記輸入,並輸出語法對象樹,然后您可以對其進行進一步分析或轉換。 編譯成字節碼更進一步,使用該AST創建一個Python代碼對象,您可以選擇使用eval()exec()函數執行該對象; 請注意,后者始終返回None ,並且可能不是評估表達式代碼對象的最佳選擇。

eval(string)使用eval(compile(string, "<stdin>", "eval"))將字符串參數編譯為代碼對象,然后執行它,因此compile(string, "<stdin>", "eval")會給您相同的結果而無需執行。

如果僅表達式有效,則使用"eval"作為模式;如果要接受完整的Python 語句,則使用"exec" 如果輸入不是有效的Python表達式( "eval" )或無效的語句( "exec" ),則compile() (和ast.parse() )會引發SyntaxError異常。

演示:

>>> 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

解析AST將使您發現代碼期望能夠訪問的名稱。 您可以通過查找“ Name節點來收集名稱:

>>> 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'}

請注意,上述忽略的上下文,因此也列出了PARAMPONLY屬性名稱。 如果您需要處理具有更多上下文的語法樹,請編寫ast.NodeVisitor子類

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM