예제 #1
0
 def compare_strings(self, lhs: Value, rhs: Value, op: str,
                     line: int) -> Value:
     """Compare two strings"""
     compare_result = self.call_c(unicode_compare, [lhs, rhs], line)
     error_constant = self.add(LoadInt(-1, line, c_int_rprimitive))
     compare_error_check = self.add(
         ComparisonOp(compare_result, error_constant, ComparisonOp.EQ,
                      line))
     exception_check, propagate, final_compare = BasicBlock(), BasicBlock(
     ), BasicBlock()
     branch = Branch(compare_error_check, exception_check, final_compare,
                     Branch.BOOL_EXPR)
     branch.negated = False
     self.add(branch)
     self.activate_block(exception_check)
     check_error_result = self.call_c(err_occurred_op, [], line)
     null = self.add(LoadInt(0, line, pointer_rprimitive))
     compare_error_check = self.add(
         ComparisonOp(check_error_result, null, ComparisonOp.NEQ, line))
     branch = Branch(compare_error_check, propagate, final_compare,
                     Branch.BOOL_EXPR)
     branch.negated = False
     self.add(branch)
     self.activate_block(propagate)
     self.call_c(keep_propagating_op, [], line)
     self.goto(final_compare)
     self.activate_block(final_compare)
     op_type = ComparisonOp.EQ if op == '==' else ComparisonOp.NEQ
     return self.add(
         ComparisonOp(compare_result,
                      self.add(LoadInt(0, line, c_int_rprimitive)), op_type,
                      line))
예제 #2
0
 def check_tagged_short_int(self, val: Value, line: int) -> Value:
     """Check if a tagged integer is a short integer"""
     int_tag = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive))
     bitwise_and = self.binary_int_op(c_pyssize_t_rprimitive, val,
                                      int_tag, BinaryIntOp.AND, line)
     zero = self.add(LoadInt(0, line, rtype=c_pyssize_t_rprimitive))
     check = self.binary_int_op(bool_rprimitive, bitwise_and, zero, BinaryIntOp.EQ, line)
     return check
예제 #3
0
 def init(self, expr_reg: Value, target_type: RType, reverse: bool) -> None:
     builder = self.builder
     self.reverse = reverse
     # Define target to contain the expression, along with the index that will be used
     # for the for-loop. If we are inside of a generator function, spill these into the
     # environment class.
     self.expr_target = builder.maybe_spill(expr_reg)
     if not reverse:
         index_reg = builder.add(LoadInt(0))
     else:
         index_reg = builder.binary_op(self.load_len(), builder.add(LoadInt(1)), '-', self.line)
     self.index_target = builder.maybe_spill_assignable(index_reg)
     self.target_type = target_type
예제 #4
0
 def load_static_int(self, value: int) -> Value:
     """Loads a static integer Python 'int' object into a register."""
     if abs(value) > MAX_LITERAL_SHORT_INT:
         identifier = self.literal_static_name(value)
         return self.add(LoadGlobal(int_rprimitive, identifier, ann=value))
     else:
         return self.add(LoadInt(value))
예제 #5
0
 def test_register(self) -> None:
     op = LoadInt(5)
     self.block.ops.append(op)
     fn = FuncIR(
         FuncDecl('myfunc', None, 'mod',
                  FuncSignature([self.arg], list_rprimitive)), [self.reg],
         [self.block])
     value_names = generate_names_for_ir(fn.arg_regs, fn.blocks)
     emitter = Emitter(EmitterContext(NameGenerator([['mod']])),
                       value_names)
     generate_native_function(fn,
                              emitter,
                              'prog.py',
                              'prog',
                              optimize_int=False)
     result = emitter.fragments
     assert_string_arrays_equal([
         'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
         '    CPyTagged cpy_r_i0;\n',
         'CPyL0: ;\n',
         '    cpy_r_i0 = 10;\n',
         '}\n',
     ],
                                result,
                                msg='Generated code invalid')
예제 #6
0
    def gen_return(self, builder: 'IRBuilder', value: Value,
                   line: int) -> None:
        # Assign an invalid next label number so that the next time
        # __next__ is called, we jump to the case in which
        # StopIteration is raised.
        builder.assign(builder.fn_info.generator_class.next_label_target,
                       builder.add(LoadInt(-1)), line)

        # Raise a StopIteration containing a field for the value that
        # should be returned. Before doing so, create a new block
        # without an error handler set so that the implicitly thrown
        # StopIteration isn't caught by except blocks inside of the
        # generator function.
        builder.builder.push_error_handler(None)
        builder.goto_and_activate(BasicBlock())

        # Skip creating a traceback frame when we raise here, because
        # we don't care about the traceback frame and it is kind of
        # expensive since raising StopIteration is an extremely common
        # case.  Also we call a special internal function to set
        # StopIteration instead of using RaiseStandardError because
        # the obvious thing doesn't work if the value is a tuple
        # (???).
        builder.primitive_op(set_stop_iteration_value, [value],
                             NO_TRACEBACK_LINE_NO)
        builder.add(Unreachable())
        builder.builder.pop_error_handler()
예제 #7
0
 def test_assign(self) -> None:
     reg = register('foo')
     op1 = LoadInt(2)
     op2 = Assign(reg, op1)
     op3 = Assign(reg, op1)
     block = make_block([op1, op2, op3])
     assert generate_names_for_ir([reg], [block]) == {op1: 'i0', reg: 'foo'}
예제 #8
0
    def gen_step(self) -> None:
        builder = self.builder
        line = self.line

        # Increment curr_index register. If the range is known to fit in short ints, use
        # short ints.
        if (is_short_int_rprimitive(self.start_reg.type)
                and is_short_int_rprimitive(self.end_reg.type)):
            new_val = builder.binary_int_op(short_int_rprimitive,
                            builder.read(self.index_reg, line),
                            builder.add(LoadInt(self.step)), BinaryIntOp.ADD, line)

        else:
            new_val = builder.binary_op(
                builder.read(self.index_reg, line), builder.add(LoadInt(self.step)), '+', line)
        builder.assign(self.index_reg, new_val, line)
        builder.assign(self.index_target, new_val, line)
예제 #9
0
 def gen_step(self) -> None:
     # Step to the next item.
     builder = self.builder
     line = self.line
     step = 1 if not self.reverse else -1
     builder.assign(self.index_target, builder.primitive_op(
         unsafe_short_add,
         [builder.read(self.index_target, line),
          builder.add(LoadInt(step))], line), line)
예제 #10
0
파일: for_helpers.py 프로젝트: xw0329/mypy
 def init(self) -> None:
     builder = self.builder
     # Create a register to store the state of the loop index and
     # initialize this register along with the loop index to 0.
     zero = builder.add(LoadInt(0))
     self.index_reg = builder.maybe_spill_assignable(zero)
     self.index_target = builder.get_assignment_target(
         self.index)  # type: Union[Register, AssignmentTarget]
     builder.assign(self.index_target, zero, self.line)
예제 #11
0
 def gen_step(self) -> None:
     # Step to the next item.
     builder = self.builder
     line = self.line
     step = 1 if not self.reverse else -1
     add = builder.binary_int_op(short_int_rprimitive,
                                 builder.read(self.index_target, line),
                                 builder.add(LoadInt(step)), BinaryIntOp.ADD, line)
     builder.assign(self.index_target, add, line)
예제 #12
0
    def add_bool_branch(self, value: Value, true: BasicBlock,
                        false: BasicBlock) -> None:
        if is_runtime_subtype(value.type, int_rprimitive):
            zero = self.add(LoadInt(0))
            value = self.binary_op(value, zero, '!=', value.line)
        elif is_same_type(value.type, list_rprimitive):
            length = self.primitive_op(list_len_op, [value], value.line)
            zero = self.add(LoadInt(0))
            value = self.binary_op(length, zero, '!=', value.line)
        elif (isinstance(value.type, RInstance)
              and value.type.class_ir.is_ext_class
              and value.type.class_ir.has_method('__bool__')):
            # Directly call the __bool__ method on classes that have it.
            value = self.gen_method_call(value, '__bool__', [],
                                         bool_rprimitive, value.line)
        else:
            value_type = optional_value_type(value.type)
            if value_type is not None:
                is_none = self.binary_op(value, self.none_object(), 'is not',
                                         value.line)
                branch = Branch(is_none, true, false, Branch.BOOL_EXPR)
                self.add(branch)
                always_truthy = False
                if isinstance(value_type, RInstance):
                    # check whether X.__bool__ is always just the default (object.__bool__)
                    if (not value_type.class_ir.has_method('__bool__') and
                            value_type.class_ir.is_method_final('__bool__')):
                        always_truthy = True

                if not always_truthy:
                    # Optional[X] where X may be falsey and requires a check
                    branch.true = BasicBlock()
                    self.activate_block(branch.true)
                    # unbox_or_cast instead of coerce because we want the
                    # type to change even if it is a subtype.
                    remaining = self.unbox_or_cast(value, value_type,
                                                   value.line)
                    self.add_bool_branch(remaining, true, false)
                return
            elif not is_same_type(value.type, bool_rprimitive):
                value = self.primitive_op(bool_op, [value], value.line)
        self.add(Branch(value, true, false, Branch.BOOL_EXPR))
예제 #13
0
 def gen_step(self) -> None:
     builder = self.builder
     line = self.line
     # We can safely assume that the integer is short, since we are not going to wrap
     # around a 63-bit integer.
     # NOTE: This would be questionable if short ints could be 32 bits.
     new_val = builder.binary_int_op(short_int_rprimitive,
             builder.read(self.index_reg, line),
             builder.add(LoadInt(1)), BinaryIntOp.ADD, line)
     builder.assign(self.index_reg, new_val, line)
     builder.assign(self.index_target, new_val, line)
예제 #14
0
파일: specialize.py 프로젝트: xw0329/mypy
def translate_len(builder: IRBuilder, expr: CallExpr,
                  callee: RefExpr) -> Optional[Value]:
    # Special case builtins.len
    if (len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]):
        expr_rtype = builder.node_type(expr.args[0])
        if isinstance(expr_rtype, RTuple):
            # len() of fixed-length tuple can be trivially determined statically,
            # though we still need to evaluate it.
            builder.accept(expr.args[0])
            return builder.add(LoadInt(len(expr_rtype.types)))
    return None
예제 #15
0
 def _create_dict(self, keys: List[Value], values: List[Value],
                  line: int) -> Value:
     """Create a dictionary(possibly empty) using keys and values"""
     # keys and values should have the same number of items
     size = len(keys)
     if size > 0:
         load_size_op = self.add(LoadInt(size, -1, c_pyssize_t_rprimitive))
         # merge keys and values
         items = [i for t in list(zip(keys, values)) for i in t]
         return self.call_c(dict_build_op, [load_size_op] + items, line)
     else:
         return self.call_c(dict_new_op, [], line)
예제 #16
0
파일: for_helpers.py 프로젝트: xw0329/mypy
    def init(self, expr_reg: Value, target_type: RType) -> None:
        builder = self.builder
        self.target_type = target_type

        # We add some variables to environment class, so they can be read across yield.
        self.expr_target = builder.maybe_spill(expr_reg)
        offset_reg = builder.add(LoadInt(0))
        self.offset_target = builder.maybe_spill_assignable(offset_reg)
        self.size = builder.maybe_spill(self.load_len(self.expr_target))

        # For dict class (not a subclass) this is the dictionary itself.
        iter_reg = builder.call_c(self.dict_iter_op, [expr_reg], self.line)
        self.iter_target = builder.maybe_spill(iter_reg)
예제 #17
0
 def builtin_len(self, val: Value, line: int) -> Value:
     typ = val.type
     if is_list_rprimitive(typ) or is_tuple_rprimitive(typ):
         elem_address = self.add(GetElementPtr(val, PyVarObject, 'ob_size'))
         size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address, val))
         offset = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive))
         return self.binary_int_op(short_int_rprimitive, size_value, offset,
                                   BinaryIntOp.LEFT_SHIFT, line)
     elif is_dict_rprimitive(typ):
         size_value = self.call_c(dict_size_op, [val], line)
         offset = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive))
         return self.binary_int_op(short_int_rprimitive, size_value, offset,
                                   BinaryIntOp.LEFT_SHIFT, line)
     elif is_set_rprimitive(typ):
         elem_address = self.add(GetElementPtr(val, PySetObject, 'used'))
         size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address, val))
         offset = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive))
         return self.binary_int_op(short_int_rprimitive, size_value, offset,
                                   BinaryIntOp.LEFT_SHIFT, line)
     # generic case
     else:
         return self.call_c(generic_len_op, [val], line)
예제 #18
0
def populate_switch_for_generator_class(builder: IRBuilder) -> None:
    cls = builder.fn_info.generator_class
    line = builder.fn_info.fitem.line

    builder.activate_block(cls.switch_block)
    for label, true_block in enumerate(cls.continuation_blocks):
        false_block = BasicBlock()
        comparison = builder.binary_op(cls.next_label_reg,
                                       builder.add(LoadInt(label)), '==', line)
        builder.add_bool_branch(comparison, true_block, false_block)
        builder.activate_block(false_block)

    builder.add(
        RaiseStandardError(RaiseStandardError.STOP_ITERATION, None, line))
    builder.add(Unreachable())
예제 #19
0
 def call_c(self,
            desc: CFunctionDescription,
            args: List[Value],
            line: int,
            result_type: Optional[RType] = None) -> Value:
     # handle void function via singleton RVoid instance
     coerced = []
     # coerce fixed number arguments
     for i in range(min(len(args), len(desc.arg_types))):
         formal_type = desc.arg_types[i]
         arg = args[i]
         arg = self.coerce(arg, formal_type, line)
         coerced.append(arg)
     # reorder args if necessary
     if desc.ordering is not None:
         assert desc.var_arg_type is None
         coerced = [coerced[i] for i in desc.ordering]
     # coerce any var_arg
     var_arg_idx = -1
     if desc.var_arg_type is not None:
         var_arg_idx = len(desc.arg_types)
         for i in range(len(desc.arg_types), len(args)):
             arg = args[i]
             arg = self.coerce(arg, desc.var_arg_type, line)
             coerced.append(arg)
     # add extra integer constant if any
     for item in desc.extra_int_constants:
         val, typ = item
         extra_int_constant = self.add(LoadInt(val, line, rtype=typ))
         coerced.append(extra_int_constant)
     target = self.add(
         CallC(desc.c_function_name, coerced, desc.return_type, desc.steals,
               desc.is_borrowed, desc.error_kind, line, var_arg_idx))
     if desc.truncated_type is None:
         result = target
     else:
         truncate = self.add(
             Truncate(target, desc.return_type, desc.truncated_type))
         result = truncate
     if result_type and not is_runtime_subtype(result.type, result_type):
         if is_none_rprimitive(result_type):
             # Special case None return. The actual result may actually be a bool
             # and so we can't just coerce it.
             result = self.none()
         else:
             result = self.coerce(target, result_type, line)
     return result
예제 #20
0
파일: function.py 프로젝트: wesleyks/mypy
def emit_yield(builder: IRBuilder, val: Value, line: int) -> Value:
    retval = builder.coerce(val, builder.ret_types[-1], line)

    cls = builder.fn_info.generator_class
    # Create a new block for the instructions immediately following the yield expression, and
    # set the next label so that the next time '__next__' is called on the generator object,
    # the function continues at the new block.
    next_block = BasicBlock()
    next_label = len(cls.continuation_blocks)
    cls.continuation_blocks.append(next_block)
    builder.assign(cls.next_label_target, builder.add(LoadInt(next_label)), line)
    builder.add(Return(retval))
    builder.activate_block(next_block)

    add_raise_exception_blocks_to_generator_class(builder, line)

    assert cls.send_arg_reg is not None
    return cls.send_arg_reg
예제 #21
0
 def gen_condition(self) -> None:
     builder = self.builder
     line = self.line
     if self.reverse:
         # If we are iterating in reverse order, we obviously need
         # to check that the index is still positive. Somewhat less
         # obviously we still need to check against the length,
         # since it could shrink out from under us.
         comparison = builder.binary_op(builder.read(self.index_target, line),
                                        builder.add(LoadInt(0)), '>=', line)
         second_check = BasicBlock()
         builder.add_bool_branch(comparison, second_check, self.loop_exit)
         builder.activate_block(second_check)
     # For compatibility with python semantics we recalculate the length
     # at every iteration.
     len_reg = self.load_len()
     comparison = builder.binary_op(builder.read(self.index_target, line), len_reg, '<', line)
     builder.add_bool_branch(comparison, self.body_block, self.loop_exit)
예제 #22
0
 def test_register(self) -> None:
     self.env.temp_index = 0
     op = LoadInt(5)
     self.block.ops.append(op)
     self.env.add_op(op)
     fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive)),
                 [self.block], self.env)
     emitter = Emitter(EmitterContext(NameGenerator([['mod']])))
     generate_native_function(fn, emitter, 'prog.py', 'prog', False)
     result = emitter.fragments
     assert_string_arrays_equal(
         [
             'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
             '    CPyTagged cpy_r_i0;\n',
             'CPyL0: ;\n',
             '    cpy_r_i0 = 10;\n',
             '}\n',
         ],
         result, msg='Generated code invalid')
예제 #23
0
def instantiate_generator_class(builder: IRBuilder) -> Value:
    fitem = builder.fn_info.fitem
    generator_reg = builder.add(
        Call(builder.fn_info.generator_class.ir.ctor, [], fitem.line))

    # Get the current environment register. If the current function is nested, then the
    # generator class gets instantiated from the callable class' '__call__' method, and hence
    # we use the callable class' environment register. Otherwise, we use the original
    # function's environment register.
    if builder.fn_info.is_nested:
        curr_env_reg = builder.fn_info.callable_class.curr_env_reg
    else:
        curr_env_reg = builder.fn_info.curr_env_reg

    # Set the generator class' environment attribute to point at the environment class
    # defined in the current scope.
    builder.add(SetAttr(generator_reg, ENV_ATTR_NAME, curr_env_reg,
                        fitem.line))

    # Set the generator class' environment class' NEXT_LABEL_ATTR_NAME attribute to 0.
    zero_reg = builder.add(LoadInt(0))
    builder.add(
        SetAttr(curr_env_reg, NEXT_LABEL_ATTR_NAME, zero_reg, fitem.line))
    return generator_reg
예제 #24
0
def split_blocks_at_errors(blocks: List[BasicBlock],
                           default_error_handler: BasicBlock,
                           func_name: Optional[str]) -> List[BasicBlock]:
    new_blocks = []  # type: List[BasicBlock]

    # First split blocks on ops that may raise.
    for block in blocks:
        ops = block.ops
        block.ops = []
        cur_block = block
        new_blocks.append(cur_block)

        # If the block has an error handler specified, use it. Otherwise
        # fall back to the default.
        error_label = block.error_handler or default_error_handler
        block.error_handler = None

        for op in ops:
            target = op
            cur_block.ops.append(op)
            if isinstance(op, RegisterOp) and op.error_kind != ERR_NEVER:
                # Split
                new_block = BasicBlock()
                new_blocks.append(new_block)

                if op.error_kind == ERR_MAGIC:
                    # Op returns an error value on error that depends on result RType.
                    variant = Branch.IS_ERROR
                    negated = False
                elif op.error_kind == ERR_FALSE:
                    # Op returns a C false value on error.
                    variant = Branch.BOOL
                    negated = True
                elif op.error_kind == ERR_ALWAYS:
                    variant = Branch.BOOL
                    negated = True
                    # this is a hack to represent the always fail
                    # semantics, using a temporary bool with value false
                    tmp = LoadInt(0, rtype=bool_rprimitive)
                    cur_block.ops.append(tmp)
                    target = tmp
                else:
                    assert False, 'unknown error kind %d' % op.error_kind

                # Void ops can't generate errors since error is always
                # indicated by a special value stored in a register.
                if op.error_kind != ERR_ALWAYS:
                    assert not op.is_void, "void op generating errors?"

                branch = Branch(target,
                                true_label=error_label,
                                false_label=new_block,
                                op=variant,
                                line=op.line)
                branch.negated = negated
                if op.line != NO_TRACEBACK_LINE_NO and func_name is not None:
                    branch.traceback_entry = (func_name, op.line)
                cur_block.ops.append(branch)
                cur_block = new_block

    return new_blocks
예제 #25
0
 def false(self) -> Value:
     """Load unboxed False value (type: bool_rprimitive)."""
     return self.add(LoadInt(0, -1, bool_rprimitive))
예제 #26
0
 def unary_not(self, value: Value, line: int) -> Value:
     mask = self.add(LoadInt(1, line, rtype=bool_rprimitive))
     return self.binary_int_op(bool_rprimitive, value, mask,
                               BinaryIntOp.XOR, line)
예제 #27
0
 def new_tuple(self, items: List[Value], line: int) -> Value:
     load_size_op = self.add(LoadInt(len(items), -1,
                                     c_pyssize_t_rprimitive))
     return self.call_c(new_tuple_op, [load_size_op] + items, line)
예제 #28
0
파일: for_helpers.py 프로젝트: xw0329/mypy
def make_for_loop_generator(builder: IRBuilder,
                            index: Lvalue,
                            expr: Expression,
                            body_block: BasicBlock,
                            loop_exit: BasicBlock,
                            line: int,
                            nested: bool = False) -> 'ForGenerator':
    """Return helper object for generating a for loop over an iterable.

    If "nested" is True, this is a nested iterator such as "e" in "enumerate(e)".
    """

    rtyp = builder.node_type(expr)
    if is_sequence_rprimitive(rtyp):
        # Special case "for x in <list>".
        expr_reg = builder.accept(expr)
        target_type = builder.get_sequence_type(expr)

        for_list = ForSequence(builder, index, body_block, loop_exit, line,
                               nested)
        for_list.init(expr_reg, target_type, reverse=False)
        return for_list

    if is_dict_rprimitive(rtyp):
        # Special case "for k in <dict>".
        expr_reg = builder.accept(expr)
        target_type = builder.get_dict_key_type(expr)

        for_dict = ForDictionaryKeys(builder, index, body_block, loop_exit,
                                     line, nested)
        for_dict.init(expr_reg, target_type)
        return for_dict

    if (isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr)):
        if (expr.callee.fullname == 'builtins.range'
                and (len(expr.args) <= 2 or
                     (len(expr.args) == 3
                      and builder.extract_int(expr.args[2]) is not None))
                and set(expr.arg_kinds) == {ARG_POS}):
            # Special case "for x in range(...)".
            # We support the 3 arg form but only for int literals, since it doesn't
            # seem worth the hassle of supporting dynamically determining which
            # direction of comparison to do.
            if len(expr.args) == 1:
                start_reg = builder.add(LoadInt(0))
                end_reg = builder.accept(expr.args[0])
            else:
                start_reg = builder.accept(expr.args[0])
                end_reg = builder.accept(expr.args[1])
            if len(expr.args) == 3:
                step = builder.extract_int(expr.args[2])
                assert step is not None
                if step == 0:
                    builder.error("range() step can't be zero",
                                  expr.args[2].line)
            else:
                step = 1

            for_range = ForRange(builder, index, body_block, loop_exit, line,
                                 nested)
            for_range.init(start_reg, end_reg, step)
            return for_range

        elif (expr.callee.fullname == 'builtins.enumerate'
              and len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]
              and isinstance(index, TupleExpr) and len(index.items) == 2):
            # Special case "for i, x in enumerate(y)".
            lvalue1 = index.items[0]
            lvalue2 = index.items[1]
            for_enumerate = ForEnumerate(builder, index, body_block, loop_exit,
                                         line, nested)
            for_enumerate.init(lvalue1, lvalue2, expr.args[0])
            return for_enumerate

        elif (expr.callee.fullname == 'builtins.zip' and len(expr.args) >= 2
              and set(expr.arg_kinds) == {ARG_POS}
              and isinstance(index, TupleExpr)
              and len(index.items) == len(expr.args)):
            # Special case "for x, y in zip(a, b)".
            for_zip = ForZip(builder, index, body_block, loop_exit, line,
                             nested)
            for_zip.init(index.items, expr.args)
            return for_zip

        if (expr.callee.fullname == 'builtins.reversed' and len(expr.args) == 1
                and expr.arg_kinds == [ARG_POS]
                and is_sequence_rprimitive(rtyp)):
            # Special case "for x in reversed(<list>)".
            expr_reg = builder.accept(expr.args[0])
            target_type = builder.get_sequence_type(expr)

            for_list = ForSequence(builder, index, body_block, loop_exit, line,
                                   nested)
            for_list.init(expr_reg, target_type, reverse=True)
            return for_list
    if (isinstance(expr, CallExpr) and isinstance(expr.callee, MemberExpr)
            and not expr.args):
        # Special cases for dictionary iterator methods, like dict.items().
        rtype = builder.node_type(expr.callee.expr)
        if (is_dict_rprimitive(rtype)
                and expr.callee.name in ('keys', 'values', 'items')):
            expr_reg = builder.accept(expr.callee.expr)
            for_dict_type = None  # type: Optional[Type[ForGenerator]]
            if expr.callee.name == 'keys':
                target_type = builder.get_dict_key_type(expr.callee.expr)
                for_dict_type = ForDictionaryKeys
            elif expr.callee.name == 'values':
                target_type = builder.get_dict_value_type(expr.callee.expr)
                for_dict_type = ForDictionaryValues
            else:
                target_type = builder.get_dict_item_type(expr.callee.expr)
                for_dict_type = ForDictionaryItems
            for_dict_gen = for_dict_type(builder, index, body_block, loop_exit,
                                         line, nested)
            for_dict_gen.init(expr_reg, target_type)
            return for_dict_gen

    # Default to a generic for loop.
    expr_reg = builder.accept(expr)
    for_obj = ForIterable(builder, index, body_block, loop_exit, line, nested)
    item_type = builder._analyze_iterable_item_type(expr)
    item_rtype = builder.type_to_rtype(item_type)
    for_obj.init(expr_reg, item_rtype)
    return for_obj
예제 #29
0
    def process_iterator_tuple_assignment(self, target: AssignmentTargetTuple,
                                          rvalue_reg: Value,
                                          line: int) -> None:

        iterator = self.primitive_op(iter_op, [rvalue_reg], line)

        # This may be the whole lvalue list if there is no starred value
        split_idx = target.star_idx if target.star_idx is not None else len(
            target.items)

        # Assign values before the first starred value
        for litem in target.items[:split_idx]:
            ritem = self.primitive_op(next_op, [iterator], line)
            error_block, ok_block = BasicBlock(), BasicBlock()
            self.add(Branch(ritem, error_block, ok_block, Branch.IS_ERROR))

            self.activate_block(error_block)
            self.add(
                RaiseStandardError(RaiseStandardError.VALUE_ERROR,
                                   'not enough values to unpack', line))
            self.add(Unreachable())

            self.activate_block(ok_block)

            self.assign(litem, ritem, line)

        # Assign the starred value and all values after it
        if target.star_idx is not None:
            post_star_vals = target.items[split_idx + 1:]
            iter_list = self.primitive_op(to_list, [iterator], line)
            iter_list_len = self.primitive_op(list_len_op, [iter_list], line)
            post_star_len = self.add(LoadInt(len(post_star_vals)))
            condition = self.binary_op(post_star_len, iter_list_len, '<=',
                                       line)

            error_block, ok_block = BasicBlock(), BasicBlock()
            self.add(Branch(condition, ok_block, error_block,
                            Branch.BOOL_EXPR))

            self.activate_block(error_block)
            self.add(
                RaiseStandardError(RaiseStandardError.VALUE_ERROR,
                                   'not enough values to unpack', line))
            self.add(Unreachable())

            self.activate_block(ok_block)

            for litem in reversed(post_star_vals):
                ritem = self.primitive_op(list_pop_last, [iter_list], line)
                self.assign(litem, ritem, line)

            # Assign the starred value
            self.assign(target.items[target.star_idx], iter_list, line)

        # There is no starred value, so check if there are extra values in rhs that
        # have not been assigned.
        else:
            extra = self.primitive_op(next_op, [iterator], line)
            error_block, ok_block = BasicBlock(), BasicBlock()
            self.add(Branch(extra, ok_block, error_block, Branch.IS_ERROR))

            self.activate_block(error_block)
            self.add(
                RaiseStandardError(RaiseStandardError.VALUE_ERROR,
                                   'too many values to unpack', line))
            self.add(Unreachable())

            self.activate_block(ok_block)
예제 #30
0
 def test_load_int(self) -> None:
     self.assert_emit(LoadInt(5), "cpy_r_i0 = 10;")
     self.assert_emit(LoadInt(5, -1, c_int_rprimitive), "cpy_r_i0 = 5;")