Esempio n. 1
0
def create_symbol_table(root):
    """
    Creates a symbols table that maps each
    symbol to the scope within which it occurs.
    The symbol table has to be created in its entirety 
    first, since entities (e.g. functions, classes) can be 
    referenced before being defined.

    Similar to find_dependencies in terms of traversing
    the AST.
    TODO: refactor common stuff into separate function

    The data structure used is a hashtable where 
    names are mapped to list of scopes, i.e. a hashlist. 

    The scope can be precisely defined in terms of 
    lineno range. The alternative is to define as scope
    as, e.g. <module name>.<function name> but this can lead 
    to ambiguity since the functions etc. can be redefined.
    Also the lineno approach relies on the beginning lines
    of siblings which can lead to a larger range than actually is 
    due to whitespaces. This gets tricky because functions can 
    be used before they are defined, but not variables.  
    """

    set_depth(root, 0)
    #Initialize the stack, with the AST root
    stack = Stack(root)

    #the symbol table maps the name to the scope.
    #Any node can belong to multiple scopes, therefore this
    #is a list of scope
    symbol_table = STable()
    
    #this represents objects imported from
    #other modules
    other_modules = {}

    for node, children, ntype in stack:

        if ntype == "Import":
            #Import object has names prop which
            #is an array of names
            for name in node.names:
                #name can be the name or an alias   
                name_val = name.asname or name.name
                #insert in symbol_table                
                symbol_table[name_val] = ()

        elif ntype == "ImportFrom":
            if node.names[0].name == '*':
                try:
                    imp_mod = importlib.import_module(node.module)
                    #Add all names in imported module, except those
                    #starting with '_'
                    for name in dir(imp_mod):
                        if name[0] != '_':
                            symbol_table[name] = stack_top(scopes)

                except ImportError:
                    print "Error: local system does not have {}. Skipping!".format(node.module)
                    pass
            else:
                #TODO: store node.module
                for name in node.names:
                    #TODO: store name.name even if name.asname defined    
                    name_val = name.asname or name.name
                    symbol_table[name_val] = stack.get_scopes(src_module=node.module)

        elif ntype == "ClassDef" or ntype == "FunctionDef":   
            symbol_table[node.name] = stack.get_scopes()
        
        #NOTE: if a name is being loaded then it already exists and doesn't need
        #to be added to symbol_table
        elif ntype == "Name" and not is_load(children) and not has_global(stack.scope_tail(), node.id): 
            symbol_table[node.id] = stack.get_scopes()

        elif ntype == "arguments":
            if node.vararg: 
                symbol_table[node.vararg] = stack.get_scopes()
            if node.kwarg:
                symbol_table[node.kwarg] = stack.get_scopes()

        elif ntype == "Global":
            #add a list global vars on node on the top of  
            #the stack
            #nonlocal could be handled in similar way
            set_globals(scopes[-1], node.names)

        #set lineno property of children nodes
        set_lineno(node, children)

        for child in children[::-1]:
            #set depth of child
            set_depth(child, node.depth + 1)
            #Add children to stack
            stack.append(child)

        #Add any new scopes
        #Need to do it here since scoping_nodes are defined in their parent scope
        stack.check_and_push_scope()

    print "Symbol table is "
    print symbol_table
    return symbol_table