# HG changeset patch # User Paul Boddie # Date 1281309785 -7200 # Node ID 978dc8a73ef5ecbb6f3ea7943636e3ef78f217ed # Parent 34ebed37aecc2779b7c0a06c0f2523f9cc242d9d Removed ExtendFrame instructions for empty units. Removed superfluous instruction emission for dynamic functions without defaults. Removed operator code emission, replacing it with calls to operator module functions, replacing attribute usage where operators are used with specific usage of the operator functions now involved. Removed the superfluous _def AST node annotation, equivalent to the unit AST node annotation already in use. Changed the RSVP library to accept a dictionary of constants and to return NotImplemented for incompatible integer comparison operations. Added attribute usage pop-up elements to generated reports. Fixed the augmented assignment test and the equality comparison test. diff -r 34ebed37aecc -r 978dc8a73ef5 docs/annotations.txt --- a/docs/annotations.txt Sat Aug 07 02:06:15 2010 +0200 +++ b/docs/annotations.txt Mon Aug 09 01:23:05 2010 +0200 @@ -5,5 +5,5 @@ names found to be used with those names _attrusers defines a dictionary mapping local names to sets of nodes defining those names -_def refers to a micropython Class or Function instance _scope set as "constant", "local", "global" or "builtins" +unit refers to a micropython Class, Function or Module instance diff -r 34ebed37aecc -r 978dc8a73ef5 lib/operator.py --- a/lib/operator.py Sat Aug 07 02:06:15 2010 +0200 +++ b/lib/operator.py Mon Aug 09 01:23:05 2010 +0200 @@ -56,13 +56,64 @@ raise TypeError -# Concrete operator functions. +def unary_op(a, accessor): + + """ + A single parameterised function providing the unary operator mechanism for + the argument 'a' using the given 'accessor' to provide the method for the + operand. + """ + + # First, try and get a method for the argument, and then call it. + + try: + fn = accessor(a) + except AttributeError: + pass + else: + result = fn() + if result is not NotImplemented: + return result + + # Where no method was available, or if the method could not support the + # operation, raise an exception. + + raise TypeError + +def augassign(a, b, augmented_accessor, left_accessor, right_accessor): + + """ + A single parameterised function providing the augmented assignment mechanism + for arguments 'a' and 'b' either using 'augmented_accessor' (directly + affecting 'a') or using 'left_accessor' and 'right_accessor' (conventional + operator method accessors). + + The result of the assignment is returned. + """ + + # First, try and get a method that directly affects the assignment target. + + try: + fn = augmented_accessor(a) + except AttributeError: + pass + else: + result = fn(b) + if result is not NotImplemented: + return result + + # Otherwise, attempt a conventional binary operation. + + return binary_op(a, b, left_accessor, right_accessor) + # These functions defer method lookup by wrapping the attribute access in # lambda functions. Thus, the appropriate methods are defined locally, but no # attempt to obtain them is made until the generic function is called. -# NOTE: The compiler should make it possible for these to call the generic -# NOTE: operator implementation with no additional call overhead. +# NOTE: The compiler should make it possible for the following functions to call +# NOTE: the generic operator implementations with no additional call overhead. + +# Binary operator functions. def add(a, b): return binary_op(a, b, lambda a: a.__add__, lambda b: b.__radd__) @@ -100,4 +151,73 @@ def xor(a, b): return binary_op(a, b, lambda a: a.__xor__, lambda b: b.__rxor__) +# Unary operator functions. + +def invert(a): + return unary_op(a, lambda a: a.__invert__) + +def neg(a): + return unary_op(a, lambda a: a.__neg__) + +def pos(a): + return unary_op(a, lambda a: a.__pos__) + +# Augmented assignment functions. + +def iadd(a, b): + return augassign(a, b, lambda a: a.__iadd__, lambda a: a.__add__, lambda b: b.__radd__) + +def iand_(a, b): + return augassign(a, b, lambda a: a.__iand__, lambda a: a.__and__, lambda b: b.__rand__) + +def idiv(a, b): + return augassign(a, b, lambda a: a.__idiv__, lambda a: a.__div__, lambda b: b.__rdiv__) + +def ifloordiv(a, b): + return augassign(a, b, lambda a: a.__ifloordiv__, lambda a: a.__floordiv__, lambda b: b.__rfloordiv__) + +def ilshift(a, b): + return augassign(a, b, lambda a: a.__ilshift__, lambda a: a.__lshift__, lambda b: b.__rlshift__) + +def imod(a, b): + return augassign(a, b, lambda a: a.__imod__, lambda a: a.__mod__, lambda b: b.__rmod__) + +def imul(a, b): + return augassign(a, b, lambda a: a.__imul__, lambda a: a.__mul__, lambda b: b.__rmul__) + +def ior_(a, b): + return augassign(a, b, lambda a: a.__ior__, lambda a: a.__or__, lambda b: b.__ror__) + +def ipow(a, b): + return augassign(a, b, lambda a: a.__ipow__, lambda a: a.__pow__, lambda b: b.__rpow__) + +def irshift(a, b): + return augassign(a, b, lambda a: a.__irshift__, lambda a: a.__rshift__, lambda b: b.__rrshift__) + +def isub(a, b): + return augassign(a, b, lambda a: a.__isub__, lambda a: a.__sub__, lambda b: b.__rsub__) + +def ixor(a, b): + return augassign(a, b, lambda a: a.__ixor__, lambda a: a.__xor__, lambda b: b.__rxor__) + +# Comparison functions. + +def eq(a, b): + return binary_op(a, b, lambda a: a.__eq__, lambda b: b.__eq__) + +def ge(a, b): + return binary_op(a, b, lambda a: a.__ge__, lambda b: b.__le__) + +def gt(a, b): + return binary_op(a, b, lambda a: a.__gt__, lambda b: b.__lt__) + +def le(a, b): + return binary_op(a, b, lambda a: a.__le__, lambda b: b.__ge__) + +def lt(a, b): + return binary_op(a, b, lambda a: a.__lt__, lambda b: b.__gt__) + +def ne(a, b): + return binary_op(a, b, lambda a: a.__ne__, lambda b: b.__ne__) + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 34ebed37aecc -r 978dc8a73ef5 micropython/ast.py --- a/micropython/ast.py Sat Aug 07 02:06:15 2010 +0200 +++ b/micropython/ast.py Mon Aug 09 01:23:05 2010 +0200 @@ -287,7 +287,7 @@ for op in node.ops: op_name, next_node = op - methods = comparison_methods[op_name] + operator_fn = operator_functions.get(op_name) # Propagate the arguments as we traverse the construct. @@ -298,13 +298,15 @@ # Use the appropriate mechanism, setting the boolean status for the # comparison. - if methods is not None: - left_method, right_method = methods + if operator_fn is not None: + + # Generate function call using evaluated argument and next node. - # Generate method call using evaluated argument and next node. + temp_fn = self._generateOperatorFunction(op_name) + self._generateInvocation(temp_fn, (temp1, temp2)) + self.discard_temp(temp_fn) - temp_result = self._generateBinary(node, temp1, temp2, left_method, right_method) - self.new_op(temp_result) + temp_result = self.optimiser.optimise_temp_storage() self._generateTestBoolean(node, temp_result) self.discard_temp(temp_result) @@ -330,14 +332,7 @@ # Add arguments. # NOTE: No support for defaults. - self._startCallFunc() - self.new_op(temp2) - self.new_op(StoreFrame(0)) - self.new_op(temp1) - self.new_op(StoreFrame(1)) - self._endCallFuncArgs(2) - self._doCallFunc(temp_method) - self._endCallFunc(temp_method) + self._generateInvocation(temp_method, (temp2, temp1)) temp_result = self.get_temp() self._generateTestBoolean(node, temp_result) @@ -519,34 +514,15 @@ visitAssTuple = visitAssList def visitAugAssign(self, node): - use_binary_block = self.new_block() - end_block = self.new_block() - # Evaluate the expression. - - self.dispatch(node.expr) - temp2 = self.optimiser.optimise_temp_storage() - - # Evaluate the target. + # Find the augmented assignment function and attempt to use it. - self.dispatch(node.node) - temp1 = self.optimiser.optimise_temp_storage() - - # Find the augmented assignment method and attempt to use it. - - aug_method, (left_method, right_method) = augassign_methods[node.op] - temp_out = self._generateOpMethod(node, temp1, temp2, aug_method, use_binary_block, use_binary_block, end_block) - self.discard_temp(temp_out) # NOTE: Will re-use the same storage. - - # Where no such method exists, use the binary operator methods. - - self.set_block(use_binary_block) - temp_out = self._generateBinary(node, temp1, temp2, left_method, right_method) + temp_fn = self._getOperatorAugAssignFunction(node) + self._visitBinaryCall(node, temp_fn, node.node, node.expr) + self.discard_temp(temp_fn) # Assign the result to the name. - self.set_block(end_block) - self.new_op(temp_out) self.record_value(1) if isinstance(node.node, compiler.ast.Name): @@ -558,11 +534,6 @@ self.discard_value() - # Compilation duties... - - self.discard_temp(temp1) - self.discard_temp(temp2) - def visitClass(self, node): if not used_by_unit(node): return @@ -840,12 +811,7 @@ self.dispatch(node.expr2) temp_arg = self.optimiser.optimise_temp_storage() - self._startCallFunc() - self.new_op(temp_arg) - self.new_op(StoreFrame(0)) - self._endCallFuncArgs(1) - self._doCallFunc(temp) - self._endCallFunc(temp) + self._generateInvocation(temp, (temp_arg,)) self.discard_temp(temp_arg) diff -r 34ebed37aecc -r 978dc8a73ef5 micropython/common.py --- a/micropython/common.py Sat Aug 07 02:06:15 2010 +0200 +++ b/micropython/common.py Mon Aug 09 01:23:05 2010 +0200 @@ -216,4 +216,52 @@ "UnarySub" : "__neg__" } +operator_functions = { + + # Binary operations. + + "Add" : "add", + "Bitand" : "and_", + "Bitor" : "or_", + "Bitxor" : "xor", + "Div" : "div", + "FloorDiv" : "floordiv", + "LeftShift" : "lshift", + "Mod" : "mod", + "Mul" : "mul", + "Power" : "pow", + "RightShift" : "rshift", + "Sub" : "sub", + + # Unary operations. + + "Invert" : "invert", + "UnaryAdd" : "pos", + "UnarySub" : "neg", + + # Augmented assignment. + + "+=" : "iadd", + "-=" : "isub", + "*=" : "imul", + "/=" : "idiv", + "//=" : "ifloordiv", + "%=" : "imod", + "**=" : "ipow", + "<<=" : "ilshift", + ">>=" : "irshift", + "&=" : "iand", + "^=" : "ixor", + "|=" : "ior", + + # Comparisons. + + "==" : "eq", + "!=" : "ne", + "<" : "lt", + "<=" : "le", + ">=" : "ge", + ">" : "gt" + } + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 34ebed37aecc -r 978dc8a73ef5 micropython/data.py --- a/micropython/data.py Sat Aug 07 02:06:15 2010 +0200 +++ b/micropython/data.py Mon Aug 09 01:23:05 2010 +0200 @@ -906,7 +906,6 @@ self.name = name self.parent = parent self.astnode = node - node._def = self # Superclasses, descendants and attributes. @@ -1285,7 +1284,6 @@ self.has_dstar = has_dstar self.dynamic_def = dynamic_def self.astnode = node - node._def = self # Initialise the positional names. diff -r 34ebed37aecc -r 978dc8a73ef5 micropython/inspect.py --- a/micropython/inspect.py Sat Aug 07 02:06:15 2010 +0200 +++ b/micropython/inspect.py Mon Aug 09 01:23:05 2010 +0200 @@ -408,6 +408,12 @@ # Generic support for classes of operations. + def _ensureOperators(self): + if not self.has_key("$operator"): + module = self.importer.load("operator") + self["$operator"] = module + return self["$operator"].get_value() + def _visitUnary(self, node): "Accounting method for the unary operator 'node'." @@ -420,9 +426,9 @@ "Accounting method for the binary operator 'node'." - left_method, right_method = binary_methods[node.__class__.__name__] - self.use_name(left_method, node) - self.use_name(right_method, node) + operator_module = self._ensureOperators() + operator_fn = operator_functions[node.__class__.__name__] + self.use_specific_attribute(operator_module.full_name(), operator_fn) return self.OP(node) def _visitFunction(self, node, name): @@ -565,10 +571,9 @@ # Accounting. - aug_method, (left_method, right_method) = augassign_methods[node.op] - self.use_name(aug_method, node) - self.use_name(left_method, node) - self.use_name(right_method, node) + operator_fn = operator_functions.get(node.op) + operator_module = self._ensureOperators() + self.use_specific_attribute(operator_module.full_name(), operator_fn) # Process the assignment. @@ -676,15 +681,19 @@ for op in node.ops: op_name, next_node = op - # Get the applicable methods. + # Define name/attribute usage. + # Get the applicable operation. - methods = comparison_methods[op_name] + operator_fn = operator_functions.get(op_name) + + # For operators, reference the specific function involved. - # Define name/attribute usage. + if operator_fn is not None: + operator_module = self._ensureOperators() + self.use_specific_attribute(operator_module.full_name(), operator_fn) - if methods is not None: - self.use_name(methods[0], this_node) - self.use_name(methods[1], next_node) + # Define __contains__ usage on the next node. + elif op_name.endswith("in"): self.use_name("__contains__", next_node) diff -r 34ebed37aecc -r 978dc8a73ef5 micropython/report.py --- a/micropython/report.py Sat Aug 07 02:06:15 2010 +0200 +++ b/micropython/report.py Mon Aug 09 01:23:05 2010 +0200 @@ -51,6 +51,7 @@ } .nowrap { white-space: nowrap; } + .label { font-size: smaller; } .class { margin-top: 1em; margin-bottom: 1em; } .function { margin-top: 1em; margin-bottom: 1em; } @@ -67,36 +68,47 @@ position: absolute; top: 3ex; left: 0; color: white; + border-bottom: 0.5ex solid #000; + border-right: 0.5ex solid #000; z-index: 3; } - .scope { padding: 0.5em; background-color: #007700; } - + .assname, .name { position: relative; + background-color: #300; color: white; } + .assname:hover, .name:hover { - background-color: #003300; - padding-bottom: 1ex; + background-color: #500; + padding-top: 0.5ex; + padding-bottom: 0.5ex; z-index: 2; } + .assname:hover .popup, .name:hover .popup { display: block; } + .attrnames, + .scope { + padding: 0.5em; + background-color: #700; + } + .summary-class { - background-color: #004400; + background-color: #040; } .summary-instance { - background-color: #0000FF; + background-color: #00F; } .summary-attr { - background-color: #007700; + background-color: #070; } @@ -188,7 +200,34 @@ _popup_end = _span_end def _scope(self, scope): - self.stream.write("
%s
\n" % scope) + self.stream.write("
scope
%s
\n" % scope) + + def _assname(self, name, node): + self.stream.write("") + if hasattr(node, "_attrnames"): + attrnames = node._attrnames + self._name_start() + self.stream.write(name) + self._popup_start() + self._attrnames(attrnames[name]) + self._popup_end() + self._name_end() + else: + self._name(node.name) + self.stream.write("") + + def _attrnames(self, attrnames): + if not attrnames: + return + + self.stream.write("
attributes
") + first = 1 + for attrname in attrnames: + if not first: + self.stream.write("
") + self.stream.write(attrname) + first = 0 + self.stream.write("
\n") # Summary classes. @@ -322,8 +361,8 @@ # Use inspected details where possible. - if hasattr(node, "_def"): - cls = node._def + if hasattr(node, "unit"): + cls = node.unit bases = cls.bases self.stream.write("
\n" % cls.full_name()) else: @@ -413,8 +452,8 @@ if not used_by_unit(node): return - if hasattr(node, "_def"): - fn = node._def + if hasattr(node, "unit"): + fn = node.unit self.stream.write("
\n" % fn.full_name()) else: print "Warning: function %s not recognised!" % node.name @@ -427,7 +466,7 @@ self._object_name_def(self.module, fn, "function-name") self.stream.write("(") - self._parameters(fn) + self._parameters(fn, node) self.stream.write(")") self.stream.write(":\n") self.stream.write("
\n") @@ -669,7 +708,7 @@ self.stream.write("") def visitAssName(self, node): - self._name(node.name) + self._assname(node.name, node) def visitAssTuple(self, node): self.stream.write("") @@ -751,15 +790,15 @@ self.stream.write("") def visitLambda(self, node): - if hasattr(node, "_def"): - fn = node._def + if hasattr(node, "unit"): + fn = node.unit else: print "Warning: function %s not recognised!" % node.name return self.stream.write("") self._keyword("lambda") - self._parameters(fn) + self._parameters(fn, node) self.stream.write(": ") self.dispatch(node.code) self.stream.write("") @@ -836,7 +875,7 @@ self.dispatch(v) first = 0 - def _parameters(self, fn): + def _parameters(self, fn, node): nparams = len(fn.positional_names) ndefaults = len(fn.defaults) first_with_default = nparams - ndefaults @@ -845,7 +884,7 @@ for n, param in enumerate(fn.positional_names): if not first: self.stream.write(", ") - self._name(param) + self._assname(param, node) n_default = n - first_with_default if n_default >= 0: self._default(fn.defaults[n_default]) diff -r 34ebed37aecc -r 978dc8a73ef5 micropython/trans.py --- a/micropython/trans.py Sat Aug 07 02:06:15 2010 +0200 +++ b/micropython/trans.py Mon Aug 09 01:23:05 2010 +0200 @@ -244,6 +244,12 @@ on the 'extend' instruction. """ + # Remove any ExtendFrame instructions which do nothing. + + if self.last_op() is extend: + self.remove_op() + return + ntemp = self.max_temp_position + 1 extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code. @@ -1055,12 +1061,12 @@ self.new_op(CheckFrame((nparams, ndefaults))) - if fn.is_dynamic(): - self.new_op(LoadTemp(0)) # context provides storage - else: - self.new_op(LoadFunction(fn)) + if ndefaults > 0: + if fn.is_dynamic(): + self.new_op(LoadTemp(0)) # context provides storage + else: + self.new_op(LoadFunction(fn)) - if ndefaults > 0: self.new_op(FillDefaults((nparams, ndefaults))) # Produce the body. @@ -1208,78 +1214,32 @@ def _visitUnary(self, node): """ - _t = node.expr - try: - _result = _t.__pos__() - except AttributeError: - raise TypeError + Invoke the appropriate operator module function for the operation + represented by 'node'. """ - method = unary_methods[node.__class__.__name__] + temp_fn = self._getOperatorFunction(node) + self._visitUnaryCall(node, temp_fn, node.expr) + self.discard_temp(temp_fn) + + def _visitUnaryCall(self, node, temp_fn, expr): - handler_block = self.new_block() - else_block = self.new_block() - end_block = self.new_block() + """ + Invoke the appropriate operator module function for the operation + represented by 'node', given a 'temp_fn' reference to a function, along + with the 'expr' operand node. + """ # Evaluate and store the operand in temporary storage. - self.dispatch(node.expr) + self.dispatch(expr) temp = self.optimiser.optimise_temp_storage() - self.new_op(temp) - - # Try to get the attribute, handling exceptions. - - self.new_op(PushHandler(handler_block)) - - # Get the method on temp. - - self._generateAttr(node, method, self.attribute_load_instructions) - temp_method = self.optimiser.optimise_temp_storage() - - # Finish handling any attribute access exceptions. - - have_handler = self.new_op(PopHandler()) - - # Add arguments. - # NOTE: No support for defaults. - - self._startCallFunc() - self.new_op(temp) # Explicit context as first argument. - self.new_op(StoreFrame(0)) - self._endCallFuncArgs(1) - self._doCallFunc(temp_method) - self._endCallFunc(temp_method) - - # Store the result. - - temp_out = self.get_temp() - self.new_op(Jump(end_block)) - - # End method attempt. - - self.set_block(handler_block) - - if have_handler: - self.new_op(PopHandler()) - self._handleAttributeError(node, temp_method, else_block) - - # Raise a TypeError. - - self.set_block(else_block) - self.make_exception("TypeError", node) - self.new_op(StoreException()) - self.new_op(RaiseException()) - - # Produce the result. - - self.set_block(end_block) - self.new_op(temp_out) + self._generateInvocation(temp_fn, (temp,)) # Compilation duties... self.discard_temp(temp) - self.discard_temp(temp_out) def _visitBinaryBit(self, node): @@ -1289,36 +1249,34 @@ bitwise operators. """ + temp_fn = self._getOperatorFunction(node) left = None for right in node.nodes: if left is not None: - self._visitBinaryMethods(node, left, right) + self._visitBinaryCall(node, temp_fn, left, right) left = right + self.discard_temp(temp_fn) + def _visitBinary(self, node): """ - _t1 = node.left - _t2 = node.right - try: - _result = _t1.__add__(_t2) - if _result is NotImplemented: - raise AttributeError - except AttributeError: - try: - _result = _t2.__radd__(_t1) - if _result is NotImplemented: - raise AttributeError - except AttributeError: - raise TypeError + Invoke the appropriate operator module function for the operation + represented by 'node'. """ - self._visitBinaryMethods(node, node.left, node.right) + temp_fn = self._getOperatorFunction(node) + self._visitBinaryCall(node, temp_fn, node.left, node.right) + self.discard_temp(temp_fn) + + def _visitBinaryCall(self, node, temp_fn, left, right): - def _visitBinaryMethods(self, node, left, right): - - left_method, right_method = binary_methods[node.__class__.__name__] + """ + Invoke the appropriate operator module function for the operation + represented by 'node', given a 'temp_fn' reference to a function, along + with the 'left' and 'right' operand nodes. + """ # Evaluate and store the left operand in temporary storage. @@ -1330,117 +1288,59 @@ self.dispatch(right) temp2 = self.optimiser.optimise_temp_storage() - temp_out = self._generateBinary(node, temp1, temp2, left_method, right_method) - - # Produce the result. - - self.new_op(temp_out) + self._generateInvocation(temp_fn, (temp1, temp2)) # Compilation duties... self.discard_temp(temp1) self.discard_temp(temp2) - self.discard_temp(temp_out) - def _generateBinary(self, node, temp1, temp2, left_method, right_method): - - """ - For the given 'node', generate the binary operator pattern for the - operands 'temp1' and 'temp2', employing 'left_method' and 'right_method' - as defined for binary operators, but also used in comparisons (for which - this method is provided). - - A temporary storage reference is returned from this method. - """ - - right_block = self.new_block() - left_else_block = self.new_block() - right_else_block = self.new_block() - end_block = self.new_block() - - # Left method. - - temp_out = self._generateOpMethod(node, temp1, temp2, left_method, left_else_block, right_block, end_block) - self.discard_temp(temp_out) # NOTE: Will re-use the same storage. - - self.set_block(left_else_block) - self.new_op(ClearException()) - - # Right method. - - self.set_block(right_block) - temp_out = self._generateOpMethod(node, temp2, temp1, right_method, right_else_block, right_else_block, end_block) - - # Raise a TypeError. - - self.set_block(right_else_block) - self.make_exception("TypeError", node) - self.new_op(StoreException()) - self.new_op(RaiseException()) - - self.set_block(end_block) - return temp_out - - def _generateOpMethod(self, node, temp1, temp2, method_name, handled_block, next_method_block, end_block): + def _generateInvocation(self, temp_fn, temp_list): """ - For the given 'node', generate the operator method invocation using the - operands 'temp1' and 'temp2', employing the given 'method_name', and - jumping appropriately to 'handled_block' where an AttributeError was - handled, to 'next_method_block' where a NotImplemented result is - returned, or to 'end_block' if the method call was successful. - - A temporary storage reference is returned from this method. + Invoke the function 'temp_fn' using the operands from 'temp_list' as + arguments. """ - handler_block = self.new_block() - - # Try to get the attribute, handling exceptions. - - self.new_op(PushHandler(handler_block)) - self.new_op(temp1) - - # Get method on temp1. - - self._generateAttr(node, method_name, self.attribute_load_instructions) - temp_method = self.optimiser.optimise_temp_storage() - - # Finish handling any attribute access exceptions. - - have_handler = self.new_op(PopHandler()) - - # Add arguments. - # NOTE: No support for defaults. - self._startCallFunc() - self.new_op(temp1) - self.new_op(StoreFrame(0)) - self.new_op(temp2) - self.new_op(StoreFrame(1)) - self._endCallFuncArgs(2) - self._doCallFunc(temp_method) - self._endCallFunc(temp_method) + + for i, temp in enumerate(temp_list): + self.new_op(temp) + self.new_op(StoreFrame(i)) - # Store the result. + self._endCallFuncArgs(len(temp_list)) + self._doCallFunc(temp_fn) + self._endCallFunc(temp_fn) + + def _getOperatorFunction(self, node): - temp_out = self.get_temp() + "Return an operator function reference for the given 'node'." + + return self._generateOperatorFunction(node.__class__.__name__) + + def _getOperatorAugAssignFunction(self, node): - # Test for NotImplemented. - # Don't actually raise an exception. + """ + Return an operator augmented assignment function reference for the given + 'node'. + """ - self.new_op(TestIdentityAddress(self.importer.get_predefined_constant("NotImplemented"))) - self.new_op(JumpIfTrue(next_method_block)) - self.new_op(Jump(end_block)) + return self._generateOperatorFunction(node.op) + + def _generateOperatorFunction(self, opname): + + "Return an operator function reference for the given 'opname'." - # End method attempt. + operator_fn = operator_functions[opname] - self.set_block(handler_block) + # Get the operator module. - if have_handler: - self.new_op(PopHandler()) - self._handleAttributeError(node, temp_method, handled_block) + operator_module = self.importer.get_module("operator") - return temp_out + # Get the appropriate function from the operator module. + + self.new_op(LoadAddress(operator_module[operator_fn])) + return self.optimiser.optimise_temp_storage() def _handleAttributeError(self, node, temp_method, handled_block): @@ -1515,12 +1415,7 @@ self._generateAttr(node, "__bool__", self.attribute_load_instructions) temp_method = self.optimiser.optimise_temp_storage() - self._startCallFunc() - self.new_op(temp) - self.new_op(StoreFrame(0)) - self._endCallFuncArgs(1) - self._doCallFunc(temp_method) - self._endCallFunc(temp_method) + self._generateInvocation(temp_method, (temp,)) self.discard_temp(temp_method) diff -r 34ebed37aecc -r 978dc8a73ef5 rsvp.py --- a/rsvp.py Sat Aug 07 02:06:15 2010 +0200 +++ b/rsvp.py Mon Aug 09 01:23:05 2010 +0200 @@ -840,10 +840,11 @@ rc = program.get_raw_image() print "Initialising the machine..." importer = program.get_importer() - true_constant = importer.get_constant(True).location - false_constant = importer.get_constant(False).location + constants = {} + for x in (True, False, NotImplemented): + constants[x] = importer.get_constant(x).location rm = RSVPMachine(rc, objlist, paramlist, debug=debug, abort_upon_exception=abort_upon_exception) - library = Library(rm, true_constant, false_constant) + library = Library(rm, constants) rm.library = library rm.pc = program.code_location print "Returning program occupying %d locations." % len(rm.memory) diff -r 34ebed37aecc -r 978dc8a73ef5 rsvplib.py --- a/rsvplib.py Sat Aug 07 02:06:15 2010 +0200 +++ b/rsvplib.py Mon Aug 09 01:23:05 2010 +0200 @@ -3,7 +3,7 @@ """ A native function library for a really simple virtual processor. -Copyright (C) 2007, 2008, 2009 Paul Boddie +Copyright (C) 2007, 2008, 2009, 2010 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -26,16 +26,15 @@ "Native function implementations." - def __init__(self, machine, true_constant, false_constant): + def __init__(self, machine, constants): """ - Initialise the library with the 'machine' and the addresses - 'true_constant' and 'false_constant'. + Initialise the library with the 'machine' and the 'constants' addresses + dictionary. """ self.machine = machine - self.true_constant = true_constant - self.false_constant = false_constant + self.constants = constants # Native class constants. @@ -97,7 +96,7 @@ self.machine.result = DataValue(addr, addr) - def builtins_logical_op(self, operand_class, op, true_if_incompatible): + def builtins_logical_op(self, operand_class, op): frame = self.local_sp_stack[-1] # Get operands addresses. @@ -107,14 +106,13 @@ # Test operand suitability. # NOTE: Handle comparisons of incompatible types more appropriately. + # NOTE: Return NotImplemented. if not (self.machine._CheckInstance(left_value.ref, operand_class) and self.machine._CheckInstance(right_value.ref, operand_class)): - if true_if_incompatible: - self.machine.result = DataValue(self.true_constant, self.true_constant) - else: - self.machine.result = DataValue(self.false_constant, self.false_constant) + notimpl = self.constants[NotImplemented] + self.machine.result = DataValue(notimpl, notimpl) return # NOTE: Assume single location for data. @@ -126,9 +124,9 @@ # NOTE: The data is considered ready to use. if op(self.machine.load(left_data), self.machine.load(right_data)): - self.machine.result = DataValue(self.true_constant, self.true_constant) + self.machine.result = DataValue(self.constants[True], self.constants[True]) else: - self.machine.result = DataValue(self.false_constant, self.false_constant) + self.machine.result = DataValue(self.constants[False], self.constants[False]) # Operators. # Although this takes a short-cut by using the operator module, testing is @@ -145,40 +143,40 @@ return self.builtins_int_arithmetic_op(operator.pow) def builtins_int_lt(self): - return self.builtins_logical_op(self.int_class, operator.lt, 0) + return self.builtins_logical_op(self.int_class, operator.lt) def builtins_int_le(self): - return self.builtins_logical_op(self.int_class, operator.le, 0) + return self.builtins_logical_op(self.int_class, operator.le) def builtins_int_gt(self): - return self.builtins_logical_op(self.int_class, operator.gt, 0) + return self.builtins_logical_op(self.int_class, operator.gt) def builtins_int_ge(self): - return self.builtins_logical_op(self.int_class, operator.ge, 0) + return self.builtins_logical_op(self.int_class, operator.ge) def builtins_int_eq(self): - return self.builtins_logical_op(self.int_class, operator.eq, 0) + return self.builtins_logical_op(self.int_class, operator.eq) def builtins_int_ne(self): - return self.builtins_logical_op(self.int_class, operator.ne, 1) + return self.builtins_logical_op(self.int_class, operator.ne) def builtins_str_lt(self): - return self.builtins_logical_op(self.str_class, operator.lt, 0) + return self.builtins_logical_op(self.str_class, operator.lt) def builtins_str_le(self): - return self.builtins_logical_op(self.str_class, operator.le, 0) + return self.builtins_logical_op(self.str_class, operator.le) def builtins_str_gt(self): - return self.builtins_logical_op(self.str_class, operator.gt, 0) + return self.builtins_logical_op(self.str_class, operator.gt) def builtins_str_ge(self): - return self.builtins_logical_op(self.str_class, operator.ge, 0) + return self.builtins_logical_op(self.str_class, operator.ge) def builtins_str_eq(self): - return self.builtins_logical_op(self.str_class, operator.eq, 0) + return self.builtins_logical_op(self.str_class, operator.eq) def builtins_str_ne(self): - return self.builtins_logical_op(self.str_class, operator.ne, 1) + return self.builtins_logical_op(self.str_class, operator.ne) def builtins_int_and(self): return self.builtins_int_arithmetic_op(operator.and_) @@ -209,9 +207,9 @@ # NOTE: The data is considered ready to use. if self.machine.load(left_data) != 0: - self.machine.result = DataValue(self.true_constant, self.true_constant) + self.machine.result = DataValue(self.constants[True], self.constants[True]) else: - self.machine.result = DataValue(self.false_constant, self.false_constant) + self.machine.result = DataValue(self.constants[False], self.constants[False]) def builtins_int_neg(self): frame = self.local_sp_stack[-1] diff -r 34ebed37aecc -r 978dc8a73ef5 tests/compare_equality.py --- a/tests/compare_equality.py Sat Aug 07 02:06:15 2010 +0200 +++ b/tests/compare_equality.py Mon Aug 09 01:23:05 2010 +0200 @@ -4,13 +4,16 @@ b = 2 class X: - def __contains__(self, other): + def __eq__(self, other): + return 0 + def __ne__(self, other): return 1 x = X() result_1 = 0 result_2 = 2 +result_3 = 3 if a != x != b: result_1 = 1 @@ -18,4 +21,7 @@ if a == x != b: result_2 = 0 +if a == x: + result_3 = 0 + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 34ebed37aecc -r 978dc8a73ef5 tests/op_iadd.py --- a/tests/op_iadd.py Sat Aug 07 02:06:15 2010 +0200 +++ b/tests/op_iadd.py Mon Aug 09 01:23:05 2010 +0200 @@ -2,9 +2,13 @@ def f(a, b): a += b + return a a = 1 b = 2 a += b +result_3 = a +result_4 = f(b, 2) +result_2 = b # vim: tabstop=4 expandtab shiftwidth=4