2012-02-24 11 views
11

Il seguente è un frammento di codice Python usando l'AST e symtable pacchetti. Sto cercando di analizzare il codice e controllare i tipi. Ma io non capisco come attraversare oggetti per arrivare alla variabile reale fa riferimento.pitone pacchetto AST: attraversamento oggetto gerarchie

Il codice seguente implementa un NodeVisitor, e una funzione viene presentato al compilatore e analizzata dal compilatore e l'ast camminato. La funzione analizzata (eval_types) riceve un paio di oggetti.

Qui di seguito sono i pezzi di codice che compongono l'esempio. Ho aggiunto alcuni commenti per ogni blocco. Per eseguire il codice, i "blocchi" devono essere riassemblati.

Le importazioni e una funzione per annullare il rientro un blocco di codice per l'analisi.

import inspect 
import ast 
import symtable 
from tokenize import generate_tokens, untokenize, INDENT 
from cStringIO import StringIO 

# _dedent borrowed from the myhdl package (www.myhdl.org) 
def _dedent(s): 
    """Dedent python code string.""" 

    result = [t[:2] for t in generate_tokens(StringIO(s).readline)] 
    # set initial indent to 0 if any 
    if result[0][0] == INDENT: 
     result[0] = (INDENT, '') 
    return untokenize(result) 

Quello che segue è il visitatore del nodo, ha le generici sovraccarichi visitatori non gestite e il nome.

class NodeVisitor(ast.NodeVisitor): 
    def __init__(self, SymbolTable): 
     self.symtable = SymbolTable 
     for child in SymbolTable.get_children(): 
      self.symtable = child 
      print(child.get_symbols()) 

    def _visit_children(self, node): 
     """Determine if the node has children and visit""" 
     for _, value in ast.iter_fields(node): 
      if isinstance(value, list): 
       for item in value: 
        if isinstance(item, ast.AST): 
         print(' visit item %s' % (type(item).__name__)) 
         self.visit(item) 

      elif isinstance(value, ast.AST): 
       print(' visit value %s' % (type(value).__name__)) 
       self.visit(value) 

    def generic_visit(self, node): 
     print(type(node).__name__) 
     self._visit_children(node) 

    def visit_Name(self, node): 
     print(' variable %s type %s' % (node.id, 
             self.symtable.lookup(node.id))) 
     print(dir(self.symtable.lookup(node.id))) 

seguito sono riportati alcuni semplici classi che saranno utilizzati nella funzione che verrà analizzato e analizzati con l'AST.

class MyObj(object): 
    def __init__(self): 
     self.val = None 

class MyObjFloat(object): 
    def __init__(self): 
     self.x = 1. 

class MyObjInt(object): 
    def __init__(self): 
     self.x = 1 

class MyObjObj(object): 
    def __init__(self): 
     self.xi = MyObjInt() 
     self.xf = MyObjFloat() 

Il seguente è la funzione di test, la funzione eval_types è la funzione che verrà analizzata con l'AST.

def testFunc(x,y,xo,z): 

    def eval_types(): 
     z.val = x + y + xo.xi.x + xo.xf.x 

    return eval_types 

Il codice per eseguire l'esempio, compilare la funzione e analizzare.

if __name__ == '__main__': 
    z = MyObj() 
    print(z.val) 
    f = testFunc(1, 2, MyObjObj(), z) 
    f() 
    print(z.val) 
    s = inspect.getsource(f) 
    s = _dedent(s) 
    print(type(s)) 
    print(s) 

    SymbolTable = symtable.symtable(s,'string','exec') 
    tree = ast.parse(s) 
    v = NodeVisitor(SymbolTable) 
    v.visit(tree) 

Quanto segue è un esempio di output fino alla prima visita del nome.

Module 
    visit item FunctionDef 
FunctionDef 
    visit value arguments 
arguments 
    visit item Assign 
Assign 
    visit item Attribute 
Attribute 
    visit value Name 
    variable z type <symbol 'z'> 
['_Symbol__flags', '_Symbol__name', '_Symbol__namespaces', 
'_Symbol__scope', '__class__', '__delattr__', '__dict__', 
'__doc__', '__format__', '__getattribute__', '__hash__', 
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', 
'__repr__', '__setattr__', '__sizeof__', '__str__', 
'__subclasshook__', '__weakref__', 'get_name', 'get_namespace', 
'get_namespaces', 'is_assigned', 'is_declared_global', 
'is_free', 'is_global', 'is_imported', 'is_local', 
'is_namespace', 'is_parameter', 'is_referenced'] 

Creazione del visitatore nodo non sembra male, ma io non riesco a capire come attraversare una gerarchia di oggetti. Nel caso generale la variabile cui si accede potrebbe essere sepolto in un oggetto. Come arrivare alla variabile attuale a cui si accede dal visitatore ast? Vedo solo che un oggetto è al nodo non sono disponibili ulteriori informazioni qual è il risultato di accesso variabile.

+1

non so seguire quello che vuoi. è che non riesci a trovare ".val"? hai provato a ricorrere sotto quel nodo? è passato molto tempo da quando l'ho usato, ma mi sembra di ricordare che nel tuo caso avresti bisogno di chiamare visit_children da visit_name. –

+0

@andrewcooke Grazie per i commenti! Sì, desidero continuare l'analisi degli oggetti (z.val, xo.xi.x, xo.xf.x) e determinare ulteriori informazioni sulla variabile effettiva utilizzata nell'istruzione. Il caso semplice sarebbe, z = x + y + xo_xi_x + xo_xf_x, dove le variabili non sono incorporate (sepolte) negli oggetti e le proprietà/attributi/tipo della variabile potrebbero essere determinate nello snippet di codice sopra. Sperimenterò chiamando visit_children esplicitamente su questi tipi di nodi. Grazie ancora. –

+0

Fare piccoli (lenti) progressi nello sviluppo di una comprensione del pacchetto AST. Per questo esempio, l'oggetto "attributi" percorrerà il ** visit_Attribute **. Ora ho semplicemente bisogno di determinare quando ** visit_Name ** ha un oggetto (ha figli?), Quindi termina in visit_Attribute e lavora all'indietro (esegui il backup dell'albero). –

risposta

2

Non so se siete ancora alla ricerca di questo, ma sembra che hai solo bisogno di aggiungere un visit_Attribute e attraversare a ritroso. Se si aggiunge questo al tuo esempio:

def visit_Attribute(self, node): 
    print(' attribute %s' % node.attr) 
    self._visit_children(node) 

Poi l'uscita per xo.xf.x è:

Add 
    visit value Attribute 
    attribute x 
    visit value Attribute 
    attribute xf 
    visit value Name 
    variable xo type <symbol 'xo'> 
    visit value Load 

seconda cosa si vuole fare con questo, si sarebbe solo bisogno di memorizzare gli attributi in una lista fino viene rilevato un Name, quindi invertirli.