def single_compare(self, node): rhs = node.comparators[0] if is_obj(node.left.type): node = self.single_compare_objects(node) elif node.left.type.is_pointer and rhs.type.is_pointer: # Coerce pointers to integer values before comparing node.left = nodes.CoercionNode(node.left, Py_uintptr_t) node.comparators = [nodes.CoercionNode(rhs, Py_uintptr_t)] elif node.left.type.is_complex and rhs.type.is_complex: real1, imag1 = extract(node.left) real2, imag2 = extract(rhs) op = type(node.ops[0]) if op == ast.Eq: lhs = compare(real1, ast.Eq(), real2) rhs = compare(imag1, ast.Eq(), imag2) result = ast.BoolOp(ast.And(), [lhs, rhs]) elif op == ast.NotEq: lhs = compare(real1, ast.NotEq(), real2) rhs = compare(imag1, ast.NotEq(), imag2) result = ast.BoolOp(ast.Or(), [lhs, rhs]) else: raise NotImplementedError("ordered comparisons are not " "implemented for complex numbers") node = nodes.typednode(result, bool_) return node
def single_compare(self, node): rhs = node.comparators[0] if is_obj(node.left.type): node = self.single_compare_objects(node) elif node.left.type.is_pointer and rhs.type.is_pointer: # Coerce pointers to integer values before comparing node.left = nodes.CoercionNode(node.left, Py_uintptr_t) node.comparators = [nodes.CoercionNode(rhs, Py_uintptr_t)] elif node.left.type.is_complex and rhs.type.is_complex: real1, imag1 = extract(node.left) real2, imag2 = extract(rhs) op = type(node.ops[0]) if op == ast.Eq: lhs = compare(real1, ast.Eq(), real2) rhs = compare(imag1, ast.Eq(), imag2) result = ast.BoolOp(ast.And(), [lhs, rhs]) elif op == ast.NotEq: lhs = compare(real1, ast.NotEq(), real2) rhs = compare(imag1, ast.NotEq(), imag2) result = ast.BoolOp(ast.Or(), [lhs, rhs]) else: raise NotImplementedError("ordered comparisons are not " "implemented for complex numbers") node = nodes.typednode(result, bool_) elif node.left.type.is_complex and rhs.type.is_datetime: raise error.NumbaError( node, "datetime comparisons not yet implemented") return node
def visit_Compare(self, node): "Reduce cascaded comparisons into single comparisons" # Process children self.generic_visit(node) compare_nodes = [] comparators = map(nodes.CloneableNode, node.comparators) # Build comparison nodes left = node.left for op, right in zip(node.ops, comparators): node = ast.Compare(left=left, ops=[op], comparators=[right]) node = nodes.typednode(node, bool_) # Handle comparisons specially based on their types node = self.single_compare(node) compare_nodes.append(node) left = right.clone # AND the comparisons together node = reduce(build_boolop, reversed(compare_nodes)) return node
def DISABLED_visit_Compare(self, node): "Reduce cascaded comparisons into single comparisons" # Process children self.generic_visit(node) # TODO: We can't generate temporaries from subexpressions since # this may invalidate execution order. For now, set the type so # we can clone for c in node.comparators: c.type = None compare_nodes = [] comparators = [nodes.CloneableNode(c) for c in node.comparators] # Build comparison nodes left = node.left for op, right in zip(node.ops, comparators): node = self.ir.Compare(left=left, ops=[op], comparators=[right]) # We shouldn't need to type this... node = nodes.typednode(node, typesystem.bool_) left = right.clone compare_nodes.append(node) # AND the comparisons together boolop = lambda left, right: self.ir.BoolOp(ast.And(), [left, right]) node = reduce(boolop, reversed(compare_nodes)) return node
def visit_CoercionNode(self, node): if not isinstance(node, nodes.CoercionNode): # CoercionNode.__new__ returns the node to be coerced if it doesn't # need coercion return node node_type = node.node.type dst_type = node.dst_type if __debug__ and self.env and self.env.debug_coercions: logger.debug('coercion: %s --> %s\n%s', node_type, dst_type, utils.pformat_ast(node)) # TODO: the below is a problem due to implicit string <-> int coercions! if (node_type.is_string and dst_type.is_numeric and not (node_type.is_pointer or node_type.is_null)): if dst_type.typename in ('char', 'uchar'): raise error.NumbaError( node, "Conversion from string to (u)char not yet supported") result = self.str_to_int(dst_type, node) elif self.nopython and (is_obj(node_type) ^ is_obj(dst_type)): raise error.NumbaError( node, "Cannot coerce to or from object in " "nopython context") elif is_obj(node.dst_type) and not is_obj(node_type): node = nodes.ObjectTempNode( nodes.CoerceToObject(node.node, node.dst_type, name=node.name)) result = self.visit(node) elif is_obj(node_type) and not is_obj(node.dst_type): node = nodes.CoerceToNative(node.node, node.dst_type, name=node.name) result = self.visit(node) elif node_type.is_null: if not dst_type.is_pointer: raise error.NumbaError( node.node, "NULL must be cast or implicitly " "coerced to a pointer type") result = self.visit(nodes.NULL.coerce(dst_type)) elif node_type.is_numeric and dst_type.is_bool: to_bool = ast.Compare(node.node, [ast.NotEq()], [nodes.const(0, node_type)]) to_bool = nodes.typednode(to_bool, bool_) result = self.visit(to_bool) else: self.generic_visit(node) if dst_type == node.node.type: result = node.node else: result = node if __debug__ and self.env and self.env.debug_coercions: logger.debug('result = %s', utils.pformat_ast(result)) return result
def visit_Compare(self, node): "Reduce cascaded comparisons into single comparisons" # Process children self.generic_visit(node) compare_nodes = [] comparators = [nodes.CloneableNode(c) for c in node.comparators] if len(node.comparators) > 1: if node.type.is_array: raise error.NumbaError( node, "Cannot determine truth value of boolean array " "(use any or all)") # Build comparison nodes left = node.left for op, right in zip(node.ops, comparators): node = ast.Compare(left=left, ops=[op], comparators=[right]) # Set result type of comparison: # bool array of array comparison # bool otherwise if left.type.is_array or right.type.is_array: # array < x -> Array(bool_, array.ndim) result_type = self.env.crnt.typesystem.promote( left.type, right.type) else: result_type = bool_ nodes.typednode(node, result_type) # Handle comparisons specially based on their types node = self.single_compare(node) compare_nodes.append(node) left = right.clone # AND the comparisons together node = reduce(build_boolop, reversed(compare_nodes)) return node
def len_(typesystem, node, obj): # Simplify len(array) to ndarray.shape[0] argtype = get_type(obj) if argtype.is_array: shape_attr = nodes.ArrayAttributeNode('shape', node.args[0]) new_node = nodes.index(shape_attr, 0) return new_node elif argtype.is_string: return nodes.CoercionNode(nodes.typednode(node, size_t), Py_ssize_t) return Py_ssize_t # Object call
def visit_CoercionNode(self, node): if not isinstance(node, nodes.CoercionNode): # CoercionNode.__new__ returns the node to be coerced if it doesn't # need coercion return node node_type = node.node.type dst_type = node.dst_type if __debug__ and self.env and self.env.debug_coercions: logger.debug('coercion: %s --> %s\n%s', node_type, dst_type, utils.pformat_ast(node)) # TODO: the below is a problem due to implicit string <-> int coercions! if (node_type.is_string and dst_type.is_numeric and not (node_type.is_pointer or node_type.is_null)): if dst_type.typename in ('char', 'uchar'): raise error.NumbaError( node, "Conversion from string to (u)char not yet supported") result = self.str_to_int(dst_type, node) elif self.nopython and (is_obj(node_type) ^ is_obj(dst_type)): raise error.NumbaError(node, "Cannot coerce to or from object in " "nopython context") elif is_obj(node.dst_type) and not is_obj(node_type): node = nodes.ObjectTempNode(nodes.CoerceToObject( node.node, node.dst_type, name=node.name)) result = self.visit(node) elif is_obj(node_type) and not is_obj(node.dst_type): node = nodes.CoerceToNative(node.node, node.dst_type, name=node.name) result = self.visit(node) elif node_type.is_null: if not dst_type.is_pointer: raise error.NumbaError(node.node, "NULL must be cast or implicitly " "coerced to a pointer type") result = self.visit(nodes.NULL.coerce(dst_type)) elif node_type.is_numeric and dst_type.is_bool: to_bool = ast.Compare(node.node, [ast.NotEq()], [nodes.const(0, node_type)]) to_bool = nodes.typednode(to_bool, bool_) result = self.visit(to_bool) else: self.generic_visit(node) if dst_type == node.node.type: result = node.node else: result = node if __debug__ and self.env and self.env.debug_coercions: logger.debug('result = %s', utils.pformat_ast(result)) return result
def visit_CheckErrorNode(self, node): if node.badval is not None: badval = node.badval eq = ast.Eq() else: assert node.goodval is not None badval = node.goodval eq = ast.NotEq() test = ast.Compare(left=node.return_value, ops=[eq], comparators=[badval]) test.right = badval test = nodes.typednode(test, bool_) check = nodes.build_if(test=test, body=[node.raise_node], orelse=[]) return self.visit(check)
def single_compare(self, node): rhs = node.comparators[0] if is_obj(node.left.type): node = self.single_compare_objects(node) elif node.left.type.is_pointer and rhs.type.is_pointer: # Coerce pointers to integer values before comparing node.left = nodes.CoercionNode(node.left, Py_uintptr_t) node.comparators = [nodes.CoercionNode(rhs, Py_uintptr_t)] elif node.left.type.is_complex and rhs.type.is_complex: real1, imag1 = extract(node.left) real2, imag2 = extract(rhs) lhs = compare(real1, real2) rhs = compare(imag1, imag2) result = ast.BoolOp(ast.And(), [lhs, rhs]) node = nodes.typednode(result, bool_) return node
def rewrite_array_iteration(self, node): """ Convert 1D array iteration to for-range and indexing: for value in my_array: ... becomes for i in my_array.shape[0]: value = my_array[i] ... """ logger.debug(ast.dump(node)) orig_target = node.target orig_iter = node.iter #-------------------------------------------------------------------- # Replace node.target with a temporary #-------------------------------------------------------------------- target_name = orig_target.id + '.idx' target_temp = nodes.TempNode(Py_ssize_t) node.target = target_temp.store() #-------------------------------------------------------------------- # Create range(A.shape[0]) #-------------------------------------------------------------------- call_func = ast.Name(id='range', ctx=ast.Load()) nodes.typednode(call_func, typesystem.range_) shape_index = ast.Index(nodes.ConstNode(0, typesystem.Py_ssize_t)) shape_index.type = typesystem.npy_intp stop = ast.Subscript(value=nodes.ShapeAttributeNode(orig_iter), slice=shape_index, ctx=ast.Load()) nodes.typednode(stop, npy_intp) #-------------------------------------------------------------------- # Create range iterator and replace node.iter #-------------------------------------------------------------------- call_args = [ nodes.ConstNode(0, typesystem.Py_ssize_t), nodes.CoercionNode(stop, typesystem.Py_ssize_t), nodes.ConstNode(1, typesystem.Py_ssize_t), ] node.iter = ast.Call(func=call_func, args=call_args) nodes.typednode(node.iter, call_func.type) node.index = target_temp.load(invariant=True) #-------------------------------------------------------------------- # Add assignment to new target variable at the start of the body #-------------------------------------------------------------------- index = ast.Index(value=node.index) index.type = target_temp.type subscript = ast.Subscript(value=orig_iter, slice=index, ctx=ast.Load()) nodes.typednode(subscript, get_type(orig_iter).dtype) #-------------------------------------------------------------------- # Add assignment to new target variable at the start of the body #-------------------------------------------------------------------- assign = ast.Assign(targets=[orig_target], value=subscript) node.body = [assign] + node.body #-------------------------------------------------------------------- # Specialize new for loop through range iteration #-------------------------------------------------------------------- return self.visit(node)
def rewrite_array_iteration(self, node): """ Convert 1D array iteration to for-range and indexing: for value in my_array: ... becomes for i in my_array.shape[0]: value = my_array[i] ... """ logger.debug(ast.dump(node)) orig_target = node.target orig_iter = node.iter #-------------------------------------------------------------------- # Replace node.target with a temporary #-------------------------------------------------------------------- target_name = orig_target.id + '.idx' target_temp = nodes.TempNode(Py_ssize_t) node.target = target_temp.store() #-------------------------------------------------------------------- # Create range(A.shape[0]) #-------------------------------------------------------------------- call_func = ast.Name(id='range', ctx=ast.Load()) nodes.typednode(call_func, typesystem.range_) shape_index = ast.Index(nodes.ConstNode(0, typesystem.Py_ssize_t)) shape_index.type = typesystem.npy_intp stop = ast.Subscript(value=nodes.ShapeAttributeNode(orig_iter), slice=shape_index, ctx=ast.Load()) nodes.typednode(stop, npy_intp) #-------------------------------------------------------------------- # Create range iterator and replace node.iter #-------------------------------------------------------------------- call_args = [nodes.ConstNode(0, typesystem.Py_ssize_t), nodes.CoercionNode(stop, typesystem.Py_ssize_t), nodes.ConstNode(1, typesystem.Py_ssize_t),] node.iter = ast.Call(func=call_func, args=call_args) nodes.typednode(node.iter, call_func.type) node.index = target_temp.load(invariant=True) #-------------------------------------------------------------------- # Add assignment to new target variable at the start of the body #-------------------------------------------------------------------- index = ast.Index(value=node.index) index.type = target_temp.type subscript = ast.Subscript(value=orig_iter, slice=index, ctx=ast.Load()) nodes.typednode(subscript, get_type(orig_iter).dtype) #-------------------------------------------------------------------- # Add assignment to new target variable at the start of the body #-------------------------------------------------------------------- assign = ast.Assign(targets=[orig_target], value=subscript) node.body = [assign] + node.body #-------------------------------------------------------------------- # Specialize new for loop through range iteration #-------------------------------------------------------------------- return self.visit(node)
def build_boolop(right, left): node = ast.BoolOp(ast.And(), [left, right]) return nodes.typednode(node, bool_)
def compare(lhs, rhs): result = ast.Compare(lhs, [ast.Eq()], [rhs]) return nodes.typednode(result, bool_)
def compare(lhs, op, rhs): result = ast.Compare(lhs, [op], [rhs]) return nodes.typednode(result, bool_)
def visit_CoercionNode(self, node): if not isinstance(node, nodes.CoercionNode): # CoercionNode.__new__ returns the node to be coerced if it doesn't # need coercion return node node_type = node.node.type dst_type = node.dst_type if __debug__ and self.env and self.env.debug_coercions: logger.debug('coercion: %s --> %s\n%s', node_type, dst_type, utils.pformat_ast(node)) if self.nopython and (is_obj(node_type) ^ is_obj(dst_type)): raise error.NumbaError( node, "Cannot coerce to or from object in " "nopython context") if is_obj(node.dst_type) and not is_obj(node_type): node = nodes.ObjectTempNode( nodes.CoerceToObject(node.node, node.dst_type, name=node.name)) result = self.visit(node) elif is_obj(node_type) and not is_obj(node.dst_type): node = nodes.CoerceToNative(node.node, node.dst_type, name=node.name) result = self.visit(node) elif node_type.is_null: if not dst_type.is_pointer: raise error.NumbaError( node.node, "NULL must be cast or implicitly " "coerced to a pointer type") result = self.visit(nodes.NULL.coerce(dst_type)) elif node_type.is_numeric and dst_type.is_bool: to_bool = ast.Compare(node.node, [ast.NotEq()], [nodes.const(0, node_type)]) to_bool = nodes.typednode(to_bool, bool_) result = self.visit(to_bool) elif node_type.is_c_string and dst_type.is_numeric: # TODO: int <-> string conversions are explicit, this should not # TODO: be a coercion if self.nopython: node = nodes.CoercionNode( function_util.external_call( self.context, self.llvm_module, ('atol' if dst_type.is_int else 'atof'), args=[node.node]), dst_type, name=node.name, ) else: if dst_type.is_int: cvtobj = function_util.external_call( self.context, self.llvm_module, 'PyInt_FromString' if not PY3 else 'PyLong_FromString', args=[node.node, nodes.NULL, nodes.const(10, int_)]) else: cvtobj = function_util.external_call( self.context, self.llvm_module, 'PyFloat_FromString', args=[node.node, nodes.const(0, Py_ssize_t)]) node = nodes.CoerceToNative(nodes.ObjectTempNode(cvtobj), dst_type, name=node.name) result = self.visit(node) else: self.generic_visit(node) if dst_type == node.node.type: result = node.node else: result = node if __debug__ and self.env and self.env.debug_coercions: logger.debug('result = %s', utils.pformat_ast(result)) return result
def visit_CoercionNode(self, node): if not isinstance(node, nodes.CoercionNode): # CoercionNode.__new__ returns the node to be coerced if it doesn't # need coercion return node node_type = node.node.type dst_type = node.dst_type if __debug__ and self.env and self.env.debug_coercions: logger.debug('coercion: %s --> %s\n%s', node_type, dst_type, utils.pformat_ast(node)) if self.nopython and is_obj(node_type): raise error.NumbaError(node, "Cannot coerce to or from object in " "nopython context") if is_obj(node.dst_type) and not is_obj(node_type): node = nodes.ObjectTempNode(nodes.CoerceToObject( node.node, node.dst_type, name=node.name)) result = self.visit(node) elif is_obj(node_type) and not is_obj(node.dst_type): node = nodes.CoerceToNative(node.node, node.dst_type, name=node.name) result = self.visit(node) elif node_type.is_null: if not dst_type.is_pointer: raise error.NumbaError(node.node, "NULL must be cast or implicitly " "coerced to a pointer type") result = self.visit(nodes.NULL.coerce(dst_type)) elif node_type.is_numeric and dst_type.is_bool: to_bool = ast.Compare(node.node, [ast.NotEq()], [nodes.const(0, node_type)]) to_bool = nodes.typednode(to_bool, bool_) result = self.visit(to_bool) elif node_type.is_c_string and dst_type.is_numeric: # TODO: int <-> string conversions are explicit, this should not # TODO: be a coercion if self.nopython: node = nodes.CoercionNode( function_util.external_call( self.context, self.llvm_module, ('atol' if dst_type.is_int else 'atof'), args=[node.node]), dst_type, name=node.name,) else: if dst_type.is_int: cvtobj = function_util.external_call( self.context, self.llvm_module, 'PyInt_FromString' if not PY3 else 'PyLong_FromString', args=[node.node, nodes.NULL, nodes.const(10, int_)]) else: cvtobj = function_util.external_call( self.context, self.llvm_module, 'PyFloat_FromString', args=[node.node, nodes.const(0, Py_ssize_t)]) node = nodes.CoerceToNative(nodes.ObjectTempNode(cvtobj), dst_type, name=node.name) result = self.visit(node) else: self.generic_visit(node) if dst_type == node.node.type: result = node.node else: result = node if __debug__ and self.env and self.env.debug_coercions: logger.debug('result = %s', utils.pformat_ast(result)) return result