2017-08-11 90 views
1

使用ast.NodeTransformer時,是否有簡單的方法可以返回多個節點來替換單個節點?例如,假設我要形式Python AST NodeTransformer:返回多個節點

f(g())的所有表達式改寫爲_x1 = g(); g(_x1)

這將是很容易做到這一點,如果visit_Expr可以返回多個,而不是一個單一的節點。我似乎無法得到那個工作,所以我認爲這是而不是的方式來做到這一點。任何建議將不勝感激。

[Update] 作爲一個更新,我的這一個工作版本,其累積在一個列表中的新和舊節點,並把它們分配給封閉範圍節點(例如ForWhileModule節點等)的主體中。這絕對是一個很不方便的做法,並且懷疑有更好的辦法。我會保持這種情況,以防有人知道這種情況。

[final update]查看文檔NodeTransformer如果節點是語句集合的一部分,實際上完全可以返回節點列表。

+0

請包括你沒有得到多個節點返回工作;包括你的嘗試,所以我可以幫助你* *。 *將多個節點作爲列表返回*明確支持*。 –

回答

1

對於聲明節點,允許您返回新節點的列表。這可以讓您用多個替換單個語句。引用文檔:

對於作爲語句集合(適用於所有語句節點)的一部分的節點,訪問者也可以返回節點列表,而不僅僅是單個節點。

對於你的表情,有一個單一的頂級Expr()表達式語句節點:

>>> ast.dump(ast.parse('f(g())')) 
"Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Call(func=Name(id='g', ctx=Load()), args=[], keywords=[])], keywords=[]))])" 
>>> import ast 
>>> ast.dump(ast.parse('f(g())')) 
"Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Call(func=Name(id='g', ctx=Load()), args=[], keywords=[])], keywords=[]))])" 

因此,所有你需要做的就是提供一個visit_Expr處理程序返回的2層語句的節點列表;第一個調用g()的調用(Assign()語句節點),第二個調用f傳遞新的變量名稱(另一個Expr()語句節點)。

我會保持變壓器子類中的狀態,當您進入和離開表達式語句上下文時設置標誌,並跟蹤該上下文下的調用棧。當你再回到visit_Expr,你回你的新格局:

self._expr_statement = False 

def visit_Expr(node): 
    self._expr_statement = True 
    self.generic_visit(node) 
    self._expr_statement = False 

    if <specific state on self matches expectations>: 
     tempvar = <generated_new_name> 
     return [ 
      Assign([Name(tempvar, Store())], <inner_call>), 
      Expr(Call(
       <outer_function_name_expr>, 
       args=[Name(tempvar, Load())], 
       keyword=[])) 
     ] 
    else: 
     # no replacement takes place 
     return node 

NodeTransformer then uses that list of elements to replace the previous Expr的()`節點。

請注意,您必須仍然調用self.generic_visit(node)以確保仍然處理嵌套節點;然後將爲這些嵌套節點調用另外的visit_*方法。這樣做使得在visit_Call方法中,您可以檢查self._expr_statement標誌,然後測試是否存在嵌套調用,然後在self上存儲足夠的上下文以使visit_Expr()方法返回。

+0

謝謝。這是我收斂的解決方案:)即將發佈,但由於您明確回答了詳細信息,因此我接受了您的答覆。我錯過了關於能夠返回頂級「Expr」的多個節點的文檔中的原始點。應該檢查了源代碼。再次感謝! *並不意味着遇到「我已經解決了」,而是想要感謝,因爲這證實了我認爲有意義的東西,這是非常有用的 – JPC

+0

一個評論,我想你想調用'generic_visit'而不是'visit_Expr'中的'visit',否則就會進入無限遞歸。 'generic_visit'將爲子節點發送相應的'visit_ *',因爲正確的方法在'super().'中解析。 – JPC

+0

@JPC:好點; 'visit()'會重新訪問同一個節點。 –