Exemple #1
0
def abstract_code_from_function(func):
    '''
    Converts the body of the function to abstract code
    
    Parameters
    ----------
    func : function, str or ast.FunctionDef
        The function object to convert. Note that the arguments to the
        function are ignored.
        
    Returns
    -------
    func : AbstractCodeFunction
        The corresponding abstract code function
        
    Raises
    ------
    SyntaxError
        If unsupported features are used such as if statements or indexing.
    '''
    if callable(func):
        code = deindent(inspect.getsource(func))
        funcnode = ast.parse(code, mode='exec').body[0]
    elif isinstance(func, str):
        funcnode = ast.parse(func, mode='exec').body[0]
    elif func.__class__ is ast.FunctionDef:
        funcnode = func
    else:
        raise TypeError("Unsupported function type")
    
    if funcnode.args.vararg is not None:
        raise SyntaxError("No support for variable number of arguments")
    if funcnode.args.kwarg is not None:
        raise SyntaxError("No support for arbitrary keyword arguments")
    if len(funcnode.args.defaults):
        raise SyntaxError("No support for default values in functions")
    
    nodes = funcnode.body
    nr = NodeRenderer()
    lines = []
    return_expr = None
    for node in nodes:
        if node.__class__ is ast.Return:
            return_expr = nr.render_node(node.value)
            break
        else:
            lines.append(nr.render_node(node))
    abstract_code = '\n'.join(lines)
    try:
        # Python 2
        args = [arg.id for arg in funcnode.args.args]
    except AttributeError:
        # Python 3
        args = [arg.arg for arg in funcnode.args.args]
    name = funcnode.name
    return AbstractCodeFunction(name, args, abstract_code, return_expr)
Exemple #2
0
def substitute_abstract_code_functions(code, funcs):
    '''
    Performs inline substitution of all the functions in the code
    
    Parameters
    ----------
    code : str
        The abstract code to make inline substitutions into.
    funcs : list, dict or set of AbstractCodeFunction
        The function substitutions to use, note in the case of a dict, the
        keys are ignored and the function name is used.
        
    Returns
    -------
    code : str
        The code with inline substitutions performed.
    '''
    if isinstance(funcs, (list, set)):
        newfuncs = dict()
        for f in funcs:
            newfuncs[f.name] = f
        funcs = newfuncs
        
    code = deindent(code)
    lines = ast.parse(code, mode='exec').body

    # This is a slightly nasty hack, but basically we just check by looking at
    # the existing identifiers how many inline operations have already been
    # performed by previous calls to this function
    ids = get_identifiers(code)
    funcstarts = {}
    for func in funcs.values():
        subids = set([id for id in ids if id.startswith('_inline_'+func.name+'_')])
        subids = set([id.replace('_inline_'+func.name+'_', '') for id in subids])
        alli = []
        for subid in subids:
            p = subid.find('_')
            if p>0:
                subid = subid[:p]
            i = int(subid)
            alli.append(i)
        if len(alli)==0:
            i = 0
        else:
            i = max(alli)+1
        funcstarts[func.name] = i
    
    # Now we rewrite all the lines, replacing each line with a sequence of
    # lines performing the inlining
    newlines = []
    for line in lines:
        for func in funcs.values():
            rw = FunctionRewriter(func, funcstarts[func.name])
            line = rw.visit(line)
            newlines.extend(rw.pre)
            funcstarts[func.name] = rw.numcalls
        newlines.append(line)
        
    # Now we render to a code string
    nr = NodeRenderer()
    newcode = '\n'.join(nr.render_node(line) for line in newlines)
    
    # We recurse until no changes in the code to ensure that all functions
    # are expanded if one function refers to another, etc.
    if newcode==code:
        return newcode
    else:
        return substitute_abstract_code_functions(newcode, funcs)