var_decl.py 3.33 KB
Newer Older
1 2 3 4 5
"""Determines the declaration, r/w status, and last use of each variable"""

import ast
import sys
from .intrin import HYBRID_GLOBALS
6
from .util import _internal_assert
7 8 9 10 11 12


class PyVariableUsage(ast.NodeVisitor):
    """The vistor class to determine the declaration, r/w status, and last use of each variable"""
    #pylint: disable=invalid-name
    #pylint: disable=missing-docstring
13
    def __init__(self, args, symbols):
14 15 16 17
        self.status = {}
        self.scope_level = []
        self._args = {}
        self.args = args
18
        self.aug_assign_ = False
19
        self.symbols = symbols
20 21 22 23


    def visit_FunctionDef(self, node):
        self.scope_level.append(node)
24 25
        _internal_assert(len(node.args.args) == len(self.args), \
                '#arguments passed should be the same as #arguments defined')
26 27 28 29 30 31 32 33
        for idx, arg in enumerate(node.args.args):
            _attr = 'id' if sys.version_info[0] < 3 else 'arg' # To make py2 and 3 compatible
            self._args[getattr(arg, _attr)] = self.args[idx]
        for i in node.body:
            self.visit(i)


    def visit_For(self, node):
34 35
        _internal_assert(isinstance(node.target, ast.Name), \
                "For's iterator should be an id")
36 37 38 39 40 41 42 43 44
        self.visit(node.iter)
        self.scope_level.append(node)
        for i in node.body:
            self.visit(i)
        self.scope_level.pop()


    def visit_Call(self, node):
        #No function pointer supported so far
45
        _internal_assert(isinstance(node.func, ast.Name), "Function call should be an id")
46
        func_id = node.func.id
47 48 49 50
        _internal_assert(func_id in list(HYBRID_GLOBALS.keys()) + \
                         ['range', 'max', 'min'] + \
                         list(self.symbols.keys()), \
                         "Function call id not in intrinsics' list")
51 52 53 54
        for elem in node.args:
            self.visit(elem)


55 56 57 58 59 60
    def visit_AugAssign(self, node):
        self.aug_assign_ = True
        self.generic_visit(node)
        self.aug_assign_ = False


61 62 63 64 65 66 67 68
    def visit_Name(self, node):
        # If it is from the argument list or loop variable, we do not worry about it!
        if node.id in self._args.keys():
            return
        fors = [loop.target.id for loop in self.scope_level if isinstance(loop, ast.For)]
        if node.id in fors:
            return
        # The loop variable cannot be overwritten when iteration
69 70
        _internal_assert(not isinstance(node.ctx, ast.Store) or node.id not in fors, \
                         "Iter var cannot be overwritten")
71 72

        if node.id not in self.status.keys():
73
            _internal_assert(isinstance(node.ctx, ast.Store), \
74 75 76
                             'Undeclared variable %s' % node.id)
            if self.aug_assign_:
                raise ValueError('"First store" cannot be an AugAssign')
77 78 79 80
            self.status[node.id] = (node, self.scope_level[-1], set())
        else:
            decl, loop, usage = self.status[node.id]
            usage.add(type(node.ctx))
81 82
            _internal_assert(loop in self.scope_level,
                             "%s is used out of the scope it is defined!" % node.id)
83 84 85
            self.status[node.id] = (decl, loop, usage)


86
def determine_variable_usage(root, args, symbols):
87
    """The helper function for calling the dedicated visitor."""
88
    visitor = PyVariableUsage(args, symbols)
89 90
    visitor.visit(root)
    return visitor.status