def visit_BinOp(self, node):
        'An in-line add'
        left = self.get_rep(node.left)
        right = self.get_rep(node.right)

        # TODO: Turn this into a table lookup rather than the same thing repeated over and over
        s = deepest_scope(left, right).scope()
        if type(node.op) is ast.Add:
            r = crep.cpp_value(
                "({0}+{1})".format(left.as_cpp(), right.as_cpp()), s,
                left.cpp_type())
        elif type(node.op) is ast.Div:
            r = crep.cpp_value(
                "({0}/{1})".format(left.as_cpp(), right.as_cpp()), s,
                left.cpp_type())
        elif type(node.op) is ast.Sub:
            r = crep.cpp_value(
                "({0}/{1})".format(left.as_cpp(), right.as_cpp()), s,
                left.cpp_type())
        elif type(node.op) is ast.Mult:
            r = crep.cpp_value(
                "({0}/{1})".format(left.as_cpp(), right.as_cpp()), s,
                left.cpp_type())
        else:
            raise BaseException(
                "Binary operator {0} is not implemented.".format(type(
                    node.op)))

        # Cache the result to push it back further up.
        node.rep = r
        self._result = r
    def visit_Call_Aggregate_only(self, node: ast.Call):
        '''
        - (acc lambda): the accumulator is set to the first element, and the lambda is called to
                        update it after that. This is called `agg_only`.
        '''
        agg_lambda = node.args[0]

        # Get the sequence we are calling against and the accumulator
        seq = self.as_sequence(node.func.value)
        accumulator, accumulator_scope = self.create_accumulator(seq)

        # We have to do a simple if statement here so that the first time through we can set the
        # accumulator, and the second time we can add to it.

        is_first_iter = crep.cpp_variable(unique_name("is_first"),
                                          self._gc.current_scope(),
                                          cpp_type=ctyp.terminal('bool'),
                                          initial_value=crep.cpp_value(
                                              'true', self._gc.current_scope(),
                                              ctyp.terminal('bool')))
        accumulator_scope.declare_variable(is_first_iter)

        # Set the scope where we will be doing the accumulation
        sv = seq.sequence_value()
        if isinstance(sv, crep.cpp_sequence):
            self._gc.set_scope(sv.iterator_value().scope()[-1])
        else:
            self._gc.set_scope(sv.scope())

        # Code up if statement to select out the first element.
        if_first = statement.iftest(is_first_iter)
        self._gc.add_statement(if_first)
        self._gc.add_statement(
            statement.set_var(
                is_first_iter,
                crep.cpp_value("false", self._gc.current_scope(),
                               ctyp.terminal('bool'))))

        # Set the accumulator
        self._gc.add_statement(
            statement.set_var(accumulator, seq.sequence_value()))
        self._gc.pop_scope()

        # Now do the if statement and make the call to calculate the accumulation.
        self._gc.add_statement(statement.elsephrase())
        call = ast.Call(
            func=agg_lambda,
            args=[accumulator.as_ast(),
                  seq.sequence_value().as_ast()])
        self._gc.add_statement(
            statement.set_var(accumulator, self.get_rep(call)))

        # Finally, since this is a terminal, we need to pop off the top.
        self._gc.set_scope(accumulator_scope)

        # Cache the results in our result in case we are skipping nodes in the AST.
        node.rep = accumulator
        self._result = accumulator
def test_deepest_scope_equal():
    g = generated_code()
    s1 = statement.iftest("true")
    s2 = statement.set_var("v1", "true")
    g.add_statement(s1)
    scope_1 = g.current_scope()

    v1 = crep.cpp_value("v1", scope_1, ctyp.terminal('int'))
    v2 = crep.cpp_value("v2", scope_1, ctyp.terminal('int'))

    assert v1 == deepest_scope(v1, v2)
    assert v2 == deepest_scope(v2, v1)
    def visit_BoolOp(self, node):
        '''A bool op like And or Or on a set of values
        This is a bit more complex than just "anding" things as we want to make sure to short-circuit the
        evaluation if we need to.
        '''

        # The result of this test
        result = crep.cpp_variable(unique_name('bool_op'),
                                   self._gc.current_scope(),
                                   cpp_type='bool')
        self._gc.declare_variable(result)

        # How we check and short-circuit depends on if we are doing and or or.
        check_expr = result.as_cpp() if type(
            node.op) == ast.And else '!{0}'.format(result.as_cpp())
        check = crep.cpp_value(check_expr,
                               self._gc.current_scope(),
                               cpp_type='bool')

        first = True
        scope = self._gc.current_scope()
        for v in node.values:
            if not first:
                self._gc.add_statement(statement.iftest(check))

            rep_v = self.get_rep(v)
            self._gc.add_statement(statement.set_var(result, rep_v))

            if not first:
                self._gc.set_scope(scope)
            first = False

        # Cache result variable so those above us have something to use.
        self._result = result
        node.rep = result
    def visit_Call_Member(self, call_node):
        'Method call on an object'

        # If this is a special type of Function call that we need to work with, split out here
        # before any processing is done.
        if (call_node.func.attr == "Aggregate"):
            return self.visit_Call_Aggregate(call_node)

        # Visit everything down a level.
        # TODO: Support arguments to functions like this.
        self.generic_visit(call_node)

        # figure out what we are calling against, and the
        # method name we are going to be calling against.
        calling_against = self.get_rep(call_node.func.value)
        function_name = call_node.func.attr

        # We support member calls that directly translate only. Here, for example, this is only for
        # obj.pt() or similar. The translation is direct.
        # TODO: The iterator might be in an argument, so passing calling_against here may not be ok.
        # TODO: We have no type system, who knows what type this function returns. Assume double.
        if not isinstance(calling_against, crep.cpp_value):
            raise BaseException(
                "Do not know how to call '{0}' on '{1}'".format(
                    function_name,
                    type(calling_against).__name__))
        c_stub = calling_against.as_cpp() + (
            "->" if calling_against.is_pointer() else ".")
        result_type = determine_type_mf(calling_against.cpp_type(),
                                        function_name)
        self._result = crep.cpp_value(c_stub + function_name + "()",
                                      calling_against.scope(), result_type)
    def create_accumulator(self,
                           seq: crep.cpp_sequence,
                           initial_value=None,
                           acc_type=None):
        'Helper to create an accumulator for the Aggregate function'
        accumulator_type = acc_type if acc_type is not None else seq.sequence_value(
        ).cpp_type()
        if not check_accumulator_type(accumulator_type):
            raise BaseException(
                "Aggregate over a sequence of type '{0}' is not supported.".
                format(str(accumulator_type)))

        # Getting the scope level right is tricky. If this is a straight sequence of items, then we want the sequence level.
        # But if this is a sequence of sequences, we are aggregating over the sequence itself. So we need to do it one level
        # up from where the iterator is running on the interior sequence.
        seq_val = seq.sequence_value()
        if isinstance(seq_val, crep.cpp_sequence):
            accumulator_scope = seq_val.iterator_value().scope()[-1]
        else:
            accumulator_scope = seq.iterator_value().scope()[-1]
        accumulator = crep.cpp_variable(
            unique_name("aggResult"),
            accumulator_scope,
            accumulator_type,
            initial_value=initial_value if initial_value is not None else
            crep.cpp_value(accumulator_type.default_value(),
                           self._gc.current_scope(), accumulator_type))
        accumulator_scope.declare_variable(accumulator)

        return accumulator, accumulator_scope
    def visit_First(self, node):
        'We are in a sequence. Take the first element of the sequence and use that for future things.'

        # Make sure we are in a loop.
        seq = self.as_sequence(node.source)

        # The First terminal works by protecting the code with a if (first_time) {} block.
        # We need to declare the first_time variable outside the block where the thing we are
        # looping over here is defined. This is a little tricky, so we delegate to another method.
        loop_scope = seq.iterator_value().scope()
        outside_block_scope = loop_scope[-1]

        # Define the variable to track this outside that block.
        is_first = crep.cpp_variable(unique_name('is_first'),
                                     outside_block_scope,
                                     cpp_type=ctyp.terminal('bool'),
                                     initial_value=crep.cpp_value(
                                         'true', self._gc.current_scope(),
                                         ctyp.terminal('bool')))
        outside_block_scope.declare_variable(is_first)

        # Now, as long as is_first is true, we can execute things inside this statement.
        # The trick is putting the if statement in the right place. We need to locate it just one level
        # below where we defined the scope above.
        s = statement.iftest(is_first)
        s.add_statement(
            statement.set_var(
                is_first,
                crep.cpp_value('false', None, cpp_type=ctyp.terminal('bool'))))

        sv = seq.sequence_value()
        if isinstance(sv, crep.cpp_sequence):
            self._gc.set_scope(sv.iterator_value().scope()[-1])
        else:
            self._gc.set_scope(sv.scope())
        self._gc.add_statement(s)

        # If we just found the first sequence in a sequence, return that.
        # Otherwise return a new version of the value.
        first_value = sv if isinstance(
            sv, crep.cpp_sequence) else sv.copy_with_new_scope(
                self._gc.current_scope())

        node.rep = first_value
        self._result = first_value
    def visit_Subscript(self, node):
        'Index into an array. Check types, as tuple indexing can be very bad for us'
        v = self.get_rep(node.value)
        if not isinstance(v, crep.cpp_collection):
            raise BaseException(
                "Do not know how to take the index of type '{0}'".format(
                    v.cpp_type()))

        index = self.get_rep(node.slice)
        node.rep = crep.cpp_value("{0}.at({1})".format(v.as_cpp(),
                                                       index.as_cpp()),
                                  self._gc.current_scope(),
                                  cpp_type=v.get_element_type())
        self._result = node.rep
Exemple #9
0
def process_ast_node(visitor, gc, call_node):
    r'''Inject the proper code into the output stream to deal with this C++ code.
    
    We expect this to be run on the back-end of the system.

    visitor - The node visitor that is converting the code into C++
    gc - the generated code object that we fill with actual code
    call_node - a Call ast node, with func being a CPPCodeValue.

    Result:
    representation - A value that represents the output
    '''

    # We write everything into a new scope to prevent conflicts. So we have to declare the result ahead of time.
    cpp_ast_node = call_node.func
    result_rep = cpp_ast_node.result_rep(gc.current_scope())

    gc.declare_variable(result_rep)

    # Include files
    for i in cpp_ast_node.include_files:
        gc.add_include(i)

    # Build the dictionary for replacement for the object we are calling
    # against, if any.
    repl_list = []
    if cpp_ast_node.replacement_instance_obj is not None:
        repl_list += [(cpp_ast_node.replacement_instance_obj[0], visitor.resolve_id(cpp_ast_node.replacement_instance_obj[1]).rep.as_cpp())]

    # Process the arguments that are getting passed to the function
    for arg,dest in zip(cpp_ast_node.args, call_node.args):
        rep = visitor.get_rep(dest)
        repl_list += [(arg, rep.as_cpp())]

    # Emit the statements.
    blk = statements.block()
    visitor._gc.add_statement(blk)

    for s in cpp_ast_node.running_code:
        l = s
        for src,dest in repl_list:
            l = l.replace(src, dest)            
        blk.add_statement(statements.arbitrary_statement(l))

    # Set the result and close the scope
    blk.add_statement(statements.set_var(result_rep, cpp_value(cpp_ast_node.result, gc.current_scope(), result_rep.cpp_type())))
    gc.pop_scope()

    return result_rep
    def visit_Compare(self, node):
        'A compare between two things. Python supports more than that, but not implemented yet.'
        if len(node.ops) != 1:
            raise BaseException("Do not support 1 < a < 10 comparisons yet!")

        left = self.get_rep(node.left)
        right = self.get_rep(node.comparators[0])

        r = crep.cpp_value(
            '({0}{1}{2})'.format(left.as_cpp(),
                                 compare_operations[type(node.ops[0])],
                                 right.as_cpp()), self._gc.current_scope(),
            ctyp.terminal("bool"))
        node.rep = r
        self._result = r
    def make_sequence_from_collection(self, rep):
        '''
        Take a collection and produce a sequence. Eventually this should likely be some sort of
        plug-in architecture. But for now, we will just assume everything looks like a vector. When
        it comes time for a new type, this is where it should go.
        '''
        element_type = rep.cpp_type().element_type()
        iterator_value = crep.cpp_value(unique_name("i_obj"), None,
                                        element_type)
        l = statement.loop(iterator_value, crep.dereference_var(rep))
        self._gc.add_statement(l)
        iterator_value.reset_scope(self._gc.current_scope())

        # For a new sequence like this the sequence and iterator value are the same
        return crep.cpp_sequence(iterator_value, iterator_value)
    def visit_function_ast(self, call_node):
        'Drop-in replacement for a function'
        # Get the arguments
        cpp_func = call_node.func
        arg_reps = [self.get_rep(a) for a in call_node.args]

        # Code up a call
        # TODO: The iterator might not be Note.
        r = crep.cpp_value('{0}({1})'.format(
            cpp_func.cpp_name, ','.join(a.as_cpp() for a in arg_reps)),
                           self._gc.current_scope(),
                           cpp_type=cpp_func.cpp_return_type)

        # Include files and return the resulting expression
        for i in cpp_func.include_files:
            self._gc.add_include(i)
        call_node.rep = r
        return r
 def visit_Str(self, node):
     node.rep = crep.cpp_value('"{0}"'.format(node.s),
                               self._gc.current_scope(),
                               ctyp.terminal("string"))
     self._result = node.rep
 def visit_Num(self, node):
     node.rep = crep.cpp_value(node.n, self._gc.current_scope(),
                               guess_type_from_number(node.n))
     self._result = node.rep
Exemple #15
0
 def __init__(self, ds_url):
     self.dataset_url = ds_url
     # Set a rep for ourselves, but it should never be directly used. We are the top level sequence.
     iterator = crep.cpp_value("bogus-do-not-use", top_level_scope(), Atlas_xAOD_File_Type())
     self.rep = crep.cpp_sequence(iterator, iterator)
Exemple #16
0
def test_expression_pointer_decl():
    e2 = crep.cpp_value("dude", top_level_scope(), ctyp.terminal("int"))
    assert False == e2.is_pointer()

    e3 = crep.cpp_value("dude", top_level_scope(), ctyp.terminal("int", is_pointer=True))
    assert True == e3.is_pointer()