Permalink
Browse files

Support unary operations

  • Loading branch information...
1 parent a02e733 commit d64029770fa90e8398cd3e6967f32f13241717ad @markflorisson committed Jun 22, 2012
Showing with 79 additions and 35 deletions.
  1. +35 −27 Cython/Compiler/ExprNodes.py
  2. +31 −7 Cython/Compiler/Vector.py
  3. +1 −1 Cython/minivect
  4. +12 −0 tests/array_expressions/elementwise.pyx
@@ -790,6 +790,10 @@ def as_none_safe_node(self, message, error="PyExc_TypeError", format_args=()):
else:
return self
+ def error(self, mess):
+ error(self.pos, mess)
+ self.type = PyrexTypes.error_type
+ self.result_code = "<error>"
class AtomicExprNode(ExprNode):
# Abstract base class for expression nodes which have
@@ -7186,9 +7190,39 @@ def analyse_types(self, env):
self.is_temp = 1
elif self.is_cpp_operation():
self.analyse_cpp_operation(env)
+ elif self.operand.type.is_memoryviewslice:
+ self.analyse_memoryview_operation(env)
else:
self.analyse_c_operation(env)
+ def type_error(self):
+ if not self.operand.type.is_error:
+ error(self.pos, "Invalid operand type for '%s' (%s)" %
+ (self.operator, self.operand.type))
+ self.type = PyrexTypes.error_type
+
+ def analyse_cpp_operation(self, env):
+ type = self.operand.type
+ if type.is_ptr:
+ type = type.base_type
+ function = type.scope.lookup("operator%s" % self.operator)
+ if not function:
+ error(self.pos, "'%s' operator not defined for %s"
+ % (self.operator, type))
+ self.type_error()
+ return
+ func_type = function.type
+ if func_type.is_ptr:
+ func_type = func_type.base_type
+ self.type = func_type.return_type
+
+ def analyse_memoryview_operation(self, env):
+ if self.operator not in elementwise_unop_operators:
+ self.error("Operator must be elementwise (got '%s'" % self.operator)
+ else:
+ self.type = self.operand.type
+ self.is_elemental = True
+
def check_const(self):
return self.operand.check_const()
@@ -7220,28 +7254,6 @@ def generate_py_operation_code(self, code):
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
- def type_error(self):
- if not self.operand.type.is_error:
- error(self.pos, "Invalid operand type for '%s' (%s)" %
- (self.operator, self.operand.type))
- self.type = PyrexTypes.error_type
-
- def analyse_cpp_operation(self, env):
- type = self.operand.type
- if type.is_ptr:
- type = type.base_type
- function = type.scope.lookup("operator%s" % self.operator)
- if not function:
- error(self.pos, "'%s' operator not defined for %s"
- % (self.operator, type))
- self.type_error()
- return
- func_type = function.type
- if func_type.is_ptr:
- func_type = func_type.base_type
- self.type = func_type.return_type
-
-
class NotNode(ExprNode):
# 'not' operator
#
@@ -7425,11 +7437,6 @@ def analyse_types(self, env):
def check_const(self):
return self.operand.check_const_addr()
- def error(self, mess):
- error(self.pos, mess)
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
-
def calculate_result_code(self):
return "(&%s)" % self.operand.result()
@@ -7968,6 +7975,7 @@ def _not_in(x, seq):
'^': operator.xor,
}
+elementwise_unop_operators = set(('+', '-', '~'))
def get_compile_time_binop(node):
func = compile_time_binary_operators.get(node.operator)
View
@@ -865,12 +865,13 @@ def dispose(self, code):
node.generate_disposal_code(code)
node.free_temps(code)
-def need_wrapper_node(type):
+def need_wrapper_node(node):
"""
Return whether a Cython node that needs to be mapped to a miniast Node,
should be mapped or wrapped (i.e., should minivect or Cython generate
the code to evaluate the expression?).
"""
+ type = node.type
while True:
if type.is_ptr:
type = type.base_type
@@ -894,6 +895,13 @@ def __init__(self, env):
self.env = env
self.operands = []
+ def visit_UnopNode(self, node):
+ dtype = get_dtype(node.type)
+ node = type(node)(node.pos, type=dtype, operator=node.operator,
+ operand=self.visit(node.operand))
+ node.analyse_types(self.env)
+ return node
+
def visit_BinopNode(self, node):
dtype = get_dtype(node.type)
node = type(node)(node.pos, type=dtype, operator=node.operator,
@@ -907,6 +915,22 @@ def visit_ExprNode(self, node):
self.operands.append(node)
return node
+def elemental_dispatcher(f):
+ def wrapper_method(self, node):
+ if not node.is_elemental:
+ return self.register_operand(node)
+
+ minitype = self.map_type(node, wrap=True)
+ if need_wrapper_node(node):
+ self.may_error = True
+ return self.register_wrapper_node(node)
+
+ return f(self, node, minitype)
+
+ wrapper_method.__name__ = f.__name__
+ wrapper_method.__doc__ = f.__doc__
+ return wrapper_method
+
class ElementalMapper(specializers.ASTMapper):
"""
When some elementwise expression is found in the Cython AST, convert that
@@ -974,7 +998,6 @@ def register_wrapper_node(self, node):
transform = CythonASTInMiniastTransform(self.env)
try:
-
node = transform.visit(node)
except CompileError, e:
error(e.position, e.message_only)
@@ -1000,12 +1023,13 @@ def visit_SingleAssignmentNode(self, node):
return self.astbuilder.assign(self.visit(node.lhs.dst),
self.visit(node.rhs))
- def visit_BinopNode(self, node):
- minitype = self.map_type(node, wrap=True)
- if need_wrapper_node(node.type):
- self.may_error = True
- return self.register_wrapper_node(node)
+ @elemental_dispatcher
+ def visit_UnopNode(self, node, minitype):
+ return self.astbuilder.unop(minitype, node.operator,
+ self.visit(node.operand))
+ @elemental_dispatcher
+ def visit_BinopNode(self, node, minitype):
op1 = self.visit(node.operand1)
op2 = self.visit(node.operand2)
return self.astbuilder.binop(minitype, node.operator, op1, op2)
@@ -173,3 +173,15 @@ def test_evaluate_operands_once(int[:] m):
m[:] = m + func1() + m + func2()
m[:] = -func3() + m
return np.asarray(m)
+
+@testcase
+def test_unop_simple(fused_dtype_t[:] m):
+ """
+ >>> test_unop_simple(np.arange(10, dtype=np.longdouble))
+ array([-2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0, -2.0], dtype=float128)
+ >>> test_unop_simple(np.arange(10, dtype=np.complex128))
+ array([-2.+0.j, -2.+0.j, -2.+0.j, -2.+0.j, -2.+0.j, -2.+0.j, -2.+0.j,
+ -2.+0.j, -2.+0.j, -2.+0.j])
+ """
+ m[:] = -m --m - 2
+ return np.asarray(m)

0 comments on commit d640297

Please sign in to comment.