From: Robert Bradshaw Date: Tue, 16 Jan 2007 01:39:47 +0000 (-0800) Subject: Implemented inplace arithmetic X-Git-Tag: 0.9.6.14~29^2~202 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=dbb891dff862952bbaae4acd58623295d51b41fc;p=cython.git Implemented inplace arithmetic --- diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 1569f5e0..66c00c82 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3041,8 +3041,11 @@ class CloneNode(CoercionNode): def __init__(self, arg): CoercionNode.__init__(self, arg) - self.type = arg.type - self.result_ctype = arg.result_ctype + if hasattr(arg, 'type'): + self.type = arg.type + self.result_ctype = arg.result_ctype + if hasattr(arg, 'entry'): + self.entry = arg.entry def calculate_result_code(self): return self.arg.result_code @@ -3051,6 +3054,8 @@ class CloneNode(CoercionNode): self.type = self.arg.type self.result_ctype = self.arg.result_ctype self.is_temp = 1 + if hasattr(self.arg, 'entry'): + self.entry = self.arg.entry #def result_as_extension_type(self): # return self.arg.result_as_extension_type() @@ -3060,6 +3065,15 @@ class CloneNode(CoercionNode): def generate_result_code(self, code): pass + + def generate_disposal_code(self, code): + code.putln("// ---- CloneNode.generate_disposal_code() for %s"%self.arg.result_code) + + def allocate_temps(self, env): + self.result_code = self.calculate_result_code() + + def release_temp(self, env): + pass #------------------------------------------------------------------------------------ # diff --git a/Cython/Compiler/Lexicon.py b/Cython/Compiler/Lexicon.py index 07ea3af1..479b6cdf 100644 --- a/Cython/Compiler/Lexicon.py +++ b/Cython/Compiler/Lexicon.py @@ -66,7 +66,7 @@ def make_lexicon(): bra = Any("([{") ket = Any(")]}") punct = Any(":,;+-*/|&<>=.%`~^?") - diphthong = Str("==", "<>", "!=", "<=", ">=", "<<", ">>", "**") + diphthong = Str("==", "<>", "!=", "<=", ">=", "<<", ">>", "**", "+=", "-=", "*=", "/=", "%=", "|=", "^=", "&=") spaces = Rep1(Any(" \t\f")) comment = Str("#") + Rep(AnyBut("\n")) escaped_newline = Str("\\\n") diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index b45909f9..3532f0f4 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2708,6 +2708,104 @@ class ParallelAssignmentNode(AssignmentNode): for stat in self.stats: stat.generate_assignment_code(code) +class InPlaceAssignmentNode(AssignmentNode): + # An in place arithmatic operand: + # + # a += b + # a -= b + # ... + # + # lhs ExprNode Left hand side + # rhs ExprNode Right hand side + # op char one of "+-*/%^&|" + # dup (ExprNode) copy of lhs used for operation (auto-generated) + # + # This code is a bit tricky because in order to obey Python + # semantics the sub-expressions (e.g. indices) of the lhs must + # not be evaluated twice. So we must re-use the values calculated + # in evaluation phase for the assignment phase as well. + # Fortunately, the type of the lhs node is fairly constrained + # (it must be a NameNode, AttributeNode, or IndexNode). + + def analyse_declarations(self, env): + self.lhs.analyse_target_declaration(env) + + def analyse_expressions_1(self, env, use_temp = 0): + import ExprNodes + self.create_dup_node(env) # re-assigns lhs to a shallow copy + self.rhs.analyse_types(env) + self.lhs.analyse_target_types(env) + if self.lhs.type.is_pyobject or self.rhs.type.is_pyobject: + self.rhs = self.rhs.coerce_to(self.lhs.type, env) + if self.lhs.type.is_pyobject: + self.result = ExprNodes.PyTempNode(self.pos, env) + self.result.allocate_temps(env) + if use_temp: + self.rhs = self.rhs.coerce_to_temp(env) + self.dup.allocate_subexpr_temps(env) + self.dup.allocate_temp(env) + self.rhs.allocate_temps(env) + + def analyse_expressions_2(self, env): + self.lhs.allocate_target_temps(env) + self.lhs.release_target_temp(env) + self.dup.release_temp(env) + if self.dup.is_temp: + self.dup.release_subexpr_temps(env) + self.rhs.release_temp(env) + if self.lhs.type.is_pyobject: + self.result.release_temp(env) + + def generate_execution_code(self, code): + self.rhs.generate_evaluation_code(code) + self.dup.generate_subexpr_evaluation_code(code) + self.dup.generate_result_code(code) + if self.lhs.type.is_pyobject: + code.putln("//---- iadd code"); + code.putln( + "%s = %s(%s, %s); if (!%s) %s" % ( + self.result.result_code, + self.py_operation_function(), + self.dup.py_result(), + self.rhs.py_result(), + self.result.py_result(), + code.error_goto(self.pos))) + self.rhs.generate_disposal_code(code) + self.dup.generate_disposal_code(code) + self.lhs.generate_assignment_code(self.result, code) + else: + # have to do assignment directly to avoid side-effects + code.putln("%s %s= %s;" % (self.lhs.result_code, self.operator, self.rhs.result_code) ) + self.rhs.generate_disposal_code(code) + if self.dup.is_temp: + self.dup.generate_subexpr_disposal_code(code) + + def create_dup_node(self, env): + import ExprNodes + self.dup = self.lhs + self.dup.analyse_types(env) + if isinstance(self.lhs, ExprNodes.NameNode): + target_lhs = ExprNodes.NameNode(self.dup.pos, name = self.dup.name, is_temp = self.dup.is_temp, entry = self.dup.entry) + elif isinstance(self.lhs, ExprNodes.AttributeNode): + target_lhs = ExprNodes.AttributeNode(self.dup.pos, obj = ExprNodes.CloneNode(self.lhs.obj), attribute = self.dup.attribute, is_temp = self.dup.is_temp) + elif isinstance(self.lhs, ExprNodes.IndexNode): + target_lhs = ExprNodes.IndexNode(self.dup.pos, base = ExprNodes.CloneNode(self.dup.base), index = ExprNodes.CloneNode(self.lhs.index), is_temp = self.dup.is_temp) + self.lhs = target_lhs + + def py_operation_function(self): + return self.py_functions[self.operator] + + py_functions = { + "|": "PyNumber_InPlaceOr", + "^": "PyNumber_InPlaceXor", + "&": "PyNumber_InPlaceAnd", + "+": "PyNumber_InPlaceAdd", + "-": "PyNumber_InPlaceSubtract", + "*": "PyNumber_InPlaceMultiply", + "/": "PyNumber_InPlaceDivide", + "%": "PyNumber_InPlaceRemainder", + } + class PrintStatNode(StatNode): # print statement diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 3bbac250..ab1ee178 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -700,8 +700,17 @@ def p_expression_or_assignment(s): s.next() expr_list.append(p_expr(s)) if len(expr_list) == 1: - expr = expr_list[0] - return Nodes.ExprStatNode(expr.pos, expr = expr) + if re.match("[+*/\%^\&|-]=", s.sy): + lhs = expr_list[0] + if not isinstance(lhs, (ExprNodes.AttributeNode, ExprNodes.IndexNode, ExprNodes.NameNode) ): + error(lhs.pos, "Illegal operand for inplace operation.") + operator = s.sy[0] + s.next() + rhs = p_expr(s) + return Nodes.InPlaceAssignmentNode(lhs.pos, operator = operator, lhs = lhs, rhs = rhs) + else: + expr = expr_list[0] + return Nodes.ExprStatNode(expr.pos, expr = expr) else: expr_list_list = [] flatten_parallel_assignments(expr_list, expr_list_list) @@ -1835,6 +1844,7 @@ def p_module(s, pxd, full_module_name): #---------------------------------------------- def print_parse_tree(f, node, level, key = None): + from Nodes import Node ind = " " * level if node: f.write(ind)