Ejemplo n.º 1
0
    def visit_If(self, node):
        if not coloring.is_green(node):
            return node

        node.body = self._transform_sequence(node.body)
        node.orelse = self._transform_sequence(node.orelse)

        # transform node test, e.g. if it's a call, this makes sure it's lifted
        node.test = self.visit(node.test)

        # lift the test (possibly again). We simply always do this,
        # because we can't know whether the value in it is a bool or
        # not.
        #
        # TODO: we can be a lot smarter when there are
        # Compare(...constant..) exprs, and transform to SFN's string
        # equality, numeric equality etc operators
        testVar = gensym.gensym("test")
        lifted = ast.Assign(targets=[ast.Name(id=testVar, ctx=ast.Store())],
                            value=ast.Call(func=ast.Name(id='bool',
                                                         ctx=ast.Load()),
                                           args=[node.test],
                                           keywords=[]))
        ast.copy_location(lifted, node.test)

        self.pre_statements.append(lifted)
        node.test = ast.copy_location(ast.Name(id=testVar, ctx=ast.Load()),
                                      node)

        return node
Ejemplo n.º 2
0
 def visit_Try(self, node):
     if not coloring.is_green(node):
         return
     node.body = self._transform_sequence(node.body)
     for h in node.handlers:
         h.body = self._transform_sequence(h.body)
     return node
Ejemplo n.º 3
0
def transform_node(node: ast.AST) -> CAST:
    if not coloring.is_green(node):
        return PythonAST([node]).set_loc(node)
    else:
        #print("Transforming a %s" % node.__class__.__name__)
        n = transform_green_node(node)
        if not isinstance(node, ast.Module):
            n.set_loc(node)
        return n
Ejemplo n.º 4
0
 def visit_Expr(self, node):
     # Special-case void-context calls.  This avoids a whole
     # unnecessary lambda for the very common case of invoking a
     # task and discarding the result.
     v = node.value
     if isinstance(v, ast.Call):
         if not coloring.is_green(v):
             return
         self._lift_call_args(v)
     return node
Ejemplo n.º 5
0
    def visit_Return(self, node):
        # blue returns are never transformed;
        # green returns are transformed UNLESS:
        #   (a) they return a constant primitive value (num, string, bool, nothing)
        #   (b) they return a variable reference (with no other expression)

        if not coloring.is_green(node):
            return

        v = node.value
        if v == None:
            return node

        if isinstance(v, ast.Num) or isinstance(v, ast.Str) or isinstance(
                v, ast.NameConstant):
            return node

        if isinstance(v, ast.Name) and isinstance(v.ctx, ast.Load):
            return node

        # return <expr> becomes:
        #  ...
        #  env[var] = transform[<expr>]
        #  return env[var]
        #
        tmpVar = gensym.gensym("ret")

        # node.value may contain green nodes, so we need to visit it
        retvalue = self.visit(node.value)

        if isinstance(retvalue, ast.Name) and isinstance(
                retvalue.ctx, ast.Load):
            # no further transform needed
            replacement = ast.copy_location(ast.Return(value=retvalue), node)
            replacement.color = True
            return replacement

        # we got something more than a name (some sort of expression).
        # lift it out to an assignment.
        lifted = ast.Assign(targets=[ast.Name(id=tmpVar, ctx=ast.Store())],
                            value=retvalue)
        ast.copy_location(lifted, node)
        # I think we don't need to color this because whatever green
        # is in there has been lifted?  Need to prove/disprove this.
        self.pre_statements.append(lifted)
        replacement = ast.Return(value=ast.Name(id=tmpVar, ctx=ast.Load()))
        ast.copy_location(replacement, node)
        replacement.color = True
        return replacement
Ejemplo n.º 6
0
    def visit_Assign(self, node):
        # assignment transforms:
        # if blue do nothing
        # if green and rhs is Call, do nothing
        # if green and rhs is anything else (e.g. a binop), change
        #   color to blue: the call will be lifted out of this assign,
        #   whatever expr it's in.
        if not coloring.is_green(node):
            return node

        if isinstance(node.value, ast.Call):
            return node

        node.color = False

        return self.generic_visit(node)
Ejemplo n.º 7
0
    def visit_Call(self, node):
        """Transform a call into (a) assignments for args (b) standalone call
        (variables in, out) (c) replacement node as tmp var ref

        """
        if not coloring.is_green(node):
            return node

        self._lift_call_args(node)

        # now, the call itself
        resultVar = gensym.gensym("call")
        lifted = ast.Assign(targets=[ast.Name(id=resultVar, ctx=ast.Store())],
                            value=node)
        ast.copy_location(lifted, node)
        lifted.color = True
        self.pre_statements.append(lifted)
        replacement = ast.copy_location(ast.Name(id=resultVar, ctx=ast.Load()),
                                        node)
        return replacement
Ejemplo n.º 8
0
def transform_list(nodes: typing.List[ast.AST]) -> typing.List[CAST]:
    if nodes == None:
        return None
    result: typing.List[CAST] = []
    curr_blue = None
    for n in nodes:
        #print("--- visiting ----- %s" % n.__class__.__name__)
        if coloring.is_green(n):
            # add curr_blue list to ast
            if curr_blue != None:
                pa = PythonAST(curr_blue)
                result.append(pa)
                curr_blue = None
            # handle green node
            result.append(transform_node(n))
        else:
            if curr_blue == None:
                curr_blue = []
            curr_blue.append(n)
    if curr_blue: # last node was blue
        result.append(PythonAST(curr_blue))
    return result
Ejemplo n.º 9
0
    def visit_While(self, node):
        if not coloring.is_green(node):
            return node

        # testVar = not bool(...test)
        testVar = gensym.gensym("test")
        testStatement = ast.Assign(
            targets=[ast.Name(id=testVar, ctx=ast.Store())],
            value=ast.UnaryOp(op=ast.Not(),
                              operand=ast.Call(func=ast.Name(id='bool',
                                                             ctx=ast.Load()),
                                               args=[node.test],
                                               keywords=[])))
        ast.copy_location(testStatement, node)
        # TODO testStatement needs to be colored appropriately
        # based on whether test contains green calls

        # "if <testVar>: break"
        breakNode = ast.Break()
        ast.copy_location(breakNode, node)
        breakNode.color = True
        loopIf = ast.If(test=ast.Name(id=testVar, ctx=ast.Load()),
                        body=[breakNode],
                        orelse=[])
        ast.copy_location(loopIf, node)
        loopIf.color = True

        # build the new body
        newbody = []
        newbody.append(testStatement)
        newbody.append(loopIf)
        newbody.extend(node.body)
        node.body = self._transform_sequence(newbody)

        node.test = ast.copy_location(ast.NameConstant(value=True), node)

        return node
Ejemplo n.º 10
0
 def visit_For(self, node):
     if not coloring.is_green(node):
         return node
     node.body = self._transform_sequence(node.body)
     node.iter = self.generic_visit(node.iter)
     return node
Ejemplo n.º 11
0
 def visit_FunctionDef(self, node):
     if not coloring.is_green(node):
         return node
     node.body = self._transform_sequence(node.body)
     return node
Ejemplo n.º 12
0
 def visit_FunctionDef(self, node):
     oldcolor = self.green
     self.green = coloring.is_green(node)
     self.generic_visit(node)
     self.green = oldcolor
     return node
Ejemplo n.º 13
0
def transform_green_node(node: ast.AST) -> CAST:
    if not coloring.is_green(node):
        raise Exception("Transform error")

    c = node.__class__
    if c == ast.FunctionDef:
        return Def(name = node.name,
                   args = [Arg(a.arg) for a in node.args.args],
                   body = transform_list(node.body))
    elif c == ast.For:
        return ForLoop(target = astor.to_source(node.target).rstrip(),
                       collection = astor.to_source(node.iter).rstrip(),
                       body = transform_list(node.body))
    elif c == ast.While:
        return WhileLoop(test = node.test,
                         body = transform_list(node.body))
    elif c == ast.If:
        return If(test = node.test,
                  thenBody = transform_list(node.body),
                  elseBody = transform_list(node.orelse))
    elif c == ast.Call:
        argVars = []
        # args should only be vars -- lift.calls lifts out all expressions in (green) function arguments
        # TODO: this pessimises inline constants by adding unnecessary assignments
        for a in node.args:
            argVars.append(varFromEnv(a))

        result = Call(func = astor.to_source(node.func).rstrip(),
                      args = argVars)

        keywordArgs = []
        for k in node.keywords:
            arg = k.arg.lower()
            if arg == 'timeout' or arg == 'timeoutseconds':
                result.timeoutSec = getNumber(k.value)
            elif arg == 'heartbeat' or arg == 'heartbeatseconds':
                result.heartbeatSec = getNumber(k.value)
            elif arg == 'retry':
                result.retry = getRetrier(k.value)

        return result
    
    elif c == ast.Module:
        return Module(body = transform_list(node.body))
    elif c == ast.Assign:
        # TODO for now just support single assignment
        return Assign(varFromEnv(node.targets[0]), transform_node(node.value))

    elif c == ast.Expr:
        # I'm not sure why this Expr node exists and not just directly
        # the Call or whatever.  So we don't model it in the CAST, we
        # just pass thru to whatever node is inside it (e.g. Call)
        return transform_node(node.value)

    elif c == ast.Return:
        return Return(node.value)

    elif c == ast.Break:
        return Break()

    elif c == ast.Try:
        handlers = []
        for eh in node.handlers:
            types = []
            if isinstance(eh.type, ast.Tuple):
                types += [t.id for t in eh.type.elts]
            else:
                types += [astor.to_source(eh.type).rstrip()]

            handlers.append(handler(types = types,
                                    name = eh.name,
                                    body = transform_list(eh.body)))
        return Try(body = transform_list(node.body), handlers = handlers)
    
    else:
        raise Exception("Unhandled node type: %s" % c.__name__)