1.1 --- a/docs/annotations.txt Sat Aug 07 02:06:15 2010 +0200
1.2 +++ b/docs/annotations.txt Mon Aug 09 01:23:05 2010 +0200
1.3 @@ -5,5 +5,5 @@
1.4 names found to be used with those names
1.5 _attrusers defines a dictionary mapping local names to sets of nodes
1.6 defining those names
1.7 -_def refers to a micropython Class or Function instance
1.8 _scope set as "constant", "local", "global" or "builtins"
1.9 +unit refers to a micropython Class, Function or Module instance
2.1 --- a/lib/operator.py Sat Aug 07 02:06:15 2010 +0200
2.2 +++ b/lib/operator.py Mon Aug 09 01:23:05 2010 +0200
2.3 @@ -56,13 +56,64 @@
2.4
2.5 raise TypeError
2.6
2.7 -# Concrete operator functions.
2.8 +def unary_op(a, accessor):
2.9 +
2.10 + """
2.11 + A single parameterised function providing the unary operator mechanism for
2.12 + the argument 'a' using the given 'accessor' to provide the method for the
2.13 + operand.
2.14 + """
2.15 +
2.16 + # First, try and get a method for the argument, and then call it.
2.17 +
2.18 + try:
2.19 + fn = accessor(a)
2.20 + except AttributeError:
2.21 + pass
2.22 + else:
2.23 + result = fn()
2.24 + if result is not NotImplemented:
2.25 + return result
2.26 +
2.27 + # Where no method was available, or if the method could not support the
2.28 + # operation, raise an exception.
2.29 +
2.30 + raise TypeError
2.31 +
2.32 +def augassign(a, b, augmented_accessor, left_accessor, right_accessor):
2.33 +
2.34 + """
2.35 + A single parameterised function providing the augmented assignment mechanism
2.36 + for arguments 'a' and 'b' either using 'augmented_accessor' (directly
2.37 + affecting 'a') or using 'left_accessor' and 'right_accessor' (conventional
2.38 + operator method accessors).
2.39 +
2.40 + The result of the assignment is returned.
2.41 + """
2.42 +
2.43 + # First, try and get a method that directly affects the assignment target.
2.44 +
2.45 + try:
2.46 + fn = augmented_accessor(a)
2.47 + except AttributeError:
2.48 + pass
2.49 + else:
2.50 + result = fn(b)
2.51 + if result is not NotImplemented:
2.52 + return result
2.53 +
2.54 + # Otherwise, attempt a conventional binary operation.
2.55 +
2.56 + return binary_op(a, b, left_accessor, right_accessor)
2.57 +
2.58 # These functions defer method lookup by wrapping the attribute access in
2.59 # lambda functions. Thus, the appropriate methods are defined locally, but no
2.60 # attempt to obtain them is made until the generic function is called.
2.61
2.62 -# NOTE: The compiler should make it possible for these to call the generic
2.63 -# NOTE: operator implementation with no additional call overhead.
2.64 +# NOTE: The compiler should make it possible for the following functions to call
2.65 +# NOTE: the generic operator implementations with no additional call overhead.
2.66 +
2.67 +# Binary operator functions.
2.68
2.69 def add(a, b):
2.70 return binary_op(a, b, lambda a: a.__add__, lambda b: b.__radd__)
2.71 @@ -100,4 +151,73 @@
2.72 def xor(a, b):
2.73 return binary_op(a, b, lambda a: a.__xor__, lambda b: b.__rxor__)
2.74
2.75 +# Unary operator functions.
2.76 +
2.77 +def invert(a):
2.78 + return unary_op(a, lambda a: a.__invert__)
2.79 +
2.80 +def neg(a):
2.81 + return unary_op(a, lambda a: a.__neg__)
2.82 +
2.83 +def pos(a):
2.84 + return unary_op(a, lambda a: a.__pos__)
2.85 +
2.86 +# Augmented assignment functions.
2.87 +
2.88 +def iadd(a, b):
2.89 + return augassign(a, b, lambda a: a.__iadd__, lambda a: a.__add__, lambda b: b.__radd__)
2.90 +
2.91 +def iand_(a, b):
2.92 + return augassign(a, b, lambda a: a.__iand__, lambda a: a.__and__, lambda b: b.__rand__)
2.93 +
2.94 +def idiv(a, b):
2.95 + return augassign(a, b, lambda a: a.__idiv__, lambda a: a.__div__, lambda b: b.__rdiv__)
2.96 +
2.97 +def ifloordiv(a, b):
2.98 + return augassign(a, b, lambda a: a.__ifloordiv__, lambda a: a.__floordiv__, lambda b: b.__rfloordiv__)
2.99 +
2.100 +def ilshift(a, b):
2.101 + return augassign(a, b, lambda a: a.__ilshift__, lambda a: a.__lshift__, lambda b: b.__rlshift__)
2.102 +
2.103 +def imod(a, b):
2.104 + return augassign(a, b, lambda a: a.__imod__, lambda a: a.__mod__, lambda b: b.__rmod__)
2.105 +
2.106 +def imul(a, b):
2.107 + return augassign(a, b, lambda a: a.__imul__, lambda a: a.__mul__, lambda b: b.__rmul__)
2.108 +
2.109 +def ior_(a, b):
2.110 + return augassign(a, b, lambda a: a.__ior__, lambda a: a.__or__, lambda b: b.__ror__)
2.111 +
2.112 +def ipow(a, b):
2.113 + return augassign(a, b, lambda a: a.__ipow__, lambda a: a.__pow__, lambda b: b.__rpow__)
2.114 +
2.115 +def irshift(a, b):
2.116 + return augassign(a, b, lambda a: a.__irshift__, lambda a: a.__rshift__, lambda b: b.__rrshift__)
2.117 +
2.118 +def isub(a, b):
2.119 + return augassign(a, b, lambda a: a.__isub__, lambda a: a.__sub__, lambda b: b.__rsub__)
2.120 +
2.121 +def ixor(a, b):
2.122 + return augassign(a, b, lambda a: a.__ixor__, lambda a: a.__xor__, lambda b: b.__rxor__)
2.123 +
2.124 +# Comparison functions.
2.125 +
2.126 +def eq(a, b):
2.127 + return binary_op(a, b, lambda a: a.__eq__, lambda b: b.__eq__)
2.128 +
2.129 +def ge(a, b):
2.130 + return binary_op(a, b, lambda a: a.__ge__, lambda b: b.__le__)
2.131 +
2.132 +def gt(a, b):
2.133 + return binary_op(a, b, lambda a: a.__gt__, lambda b: b.__lt__)
2.134 +
2.135 +def le(a, b):
2.136 + return binary_op(a, b, lambda a: a.__le__, lambda b: b.__ge__)
2.137 +
2.138 +def lt(a, b):
2.139 + return binary_op(a, b, lambda a: a.__lt__, lambda b: b.__gt__)
2.140 +
2.141 +def ne(a, b):
2.142 + return binary_op(a, b, lambda a: a.__ne__, lambda b: b.__ne__)
2.143 +
2.144 # vim: tabstop=4 expandtab shiftwidth=4
3.1 --- a/micropython/ast.py Sat Aug 07 02:06:15 2010 +0200
3.2 +++ b/micropython/ast.py Mon Aug 09 01:23:05 2010 +0200
3.3 @@ -287,7 +287,7 @@
3.4
3.5 for op in node.ops:
3.6 op_name, next_node = op
3.7 - methods = comparison_methods[op_name]
3.8 + operator_fn = operator_functions.get(op_name)
3.9
3.10 # Propagate the arguments as we traverse the construct.
3.11
3.12 @@ -298,13 +298,15 @@
3.13 # Use the appropriate mechanism, setting the boolean status for the
3.14 # comparison.
3.15
3.16 - if methods is not None:
3.17 - left_method, right_method = methods
3.18 + if operator_fn is not None:
3.19 +
3.20 + # Generate function call using evaluated argument and next node.
3.21
3.22 - # Generate method call using evaluated argument and next node.
3.23 + temp_fn = self._generateOperatorFunction(op_name)
3.24 + self._generateInvocation(temp_fn, (temp1, temp2))
3.25 + self.discard_temp(temp_fn)
3.26
3.27 - temp_result = self._generateBinary(node, temp1, temp2, left_method, right_method)
3.28 - self.new_op(temp_result)
3.29 + temp_result = self.optimiser.optimise_temp_storage()
3.30 self._generateTestBoolean(node, temp_result)
3.31 self.discard_temp(temp_result)
3.32
3.33 @@ -330,14 +332,7 @@
3.34 # Add arguments.
3.35 # NOTE: No support for defaults.
3.36
3.37 - self._startCallFunc()
3.38 - self.new_op(temp2)
3.39 - self.new_op(StoreFrame(0))
3.40 - self.new_op(temp1)
3.41 - self.new_op(StoreFrame(1))
3.42 - self._endCallFuncArgs(2)
3.43 - self._doCallFunc(temp_method)
3.44 - self._endCallFunc(temp_method)
3.45 + self._generateInvocation(temp_method, (temp2, temp1))
3.46
3.47 temp_result = self.get_temp()
3.48 self._generateTestBoolean(node, temp_result)
3.49 @@ -519,34 +514,15 @@
3.50 visitAssTuple = visitAssList
3.51
3.52 def visitAugAssign(self, node):
3.53 - use_binary_block = self.new_block()
3.54 - end_block = self.new_block()
3.55
3.56 - # Evaluate the expression.
3.57 -
3.58 - self.dispatch(node.expr)
3.59 - temp2 = self.optimiser.optimise_temp_storage()
3.60 -
3.61 - # Evaluate the target.
3.62 + # Find the augmented assignment function and attempt to use it.
3.63
3.64 - self.dispatch(node.node)
3.65 - temp1 = self.optimiser.optimise_temp_storage()
3.66 -
3.67 - # Find the augmented assignment method and attempt to use it.
3.68 -
3.69 - aug_method, (left_method, right_method) = augassign_methods[node.op]
3.70 - temp_out = self._generateOpMethod(node, temp1, temp2, aug_method, use_binary_block, use_binary_block, end_block)
3.71 - self.discard_temp(temp_out) # NOTE: Will re-use the same storage.
3.72 -
3.73 - # Where no such method exists, use the binary operator methods.
3.74 -
3.75 - self.set_block(use_binary_block)
3.76 - temp_out = self._generateBinary(node, temp1, temp2, left_method, right_method)
3.77 + temp_fn = self._getOperatorAugAssignFunction(node)
3.78 + self._visitBinaryCall(node, temp_fn, node.node, node.expr)
3.79 + self.discard_temp(temp_fn)
3.80
3.81 # Assign the result to the name.
3.82
3.83 - self.set_block(end_block)
3.84 - self.new_op(temp_out)
3.85 self.record_value(1)
3.86
3.87 if isinstance(node.node, compiler.ast.Name):
3.88 @@ -558,11 +534,6 @@
3.89
3.90 self.discard_value()
3.91
3.92 - # Compilation duties...
3.93 -
3.94 - self.discard_temp(temp1)
3.95 - self.discard_temp(temp2)
3.96 -
3.97 def visitClass(self, node):
3.98 if not used_by_unit(node):
3.99 return
3.100 @@ -840,12 +811,7 @@
3.101 self.dispatch(node.expr2)
3.102 temp_arg = self.optimiser.optimise_temp_storage()
3.103
3.104 - self._startCallFunc()
3.105 - self.new_op(temp_arg)
3.106 - self.new_op(StoreFrame(0))
3.107 - self._endCallFuncArgs(1)
3.108 - self._doCallFunc(temp)
3.109 - self._endCallFunc(temp)
3.110 + self._generateInvocation(temp, (temp_arg,))
3.111
3.112 self.discard_temp(temp_arg)
3.113
4.1 --- a/micropython/common.py Sat Aug 07 02:06:15 2010 +0200
4.2 +++ b/micropython/common.py Mon Aug 09 01:23:05 2010 +0200
4.3 @@ -216,4 +216,52 @@
4.4 "UnarySub" : "__neg__"
4.5 }
4.6
4.7 +operator_functions = {
4.8 +
4.9 + # Binary operations.
4.10 +
4.11 + "Add" : "add",
4.12 + "Bitand" : "and_",
4.13 + "Bitor" : "or_",
4.14 + "Bitxor" : "xor",
4.15 + "Div" : "div",
4.16 + "FloorDiv" : "floordiv",
4.17 + "LeftShift" : "lshift",
4.18 + "Mod" : "mod",
4.19 + "Mul" : "mul",
4.20 + "Power" : "pow",
4.21 + "RightShift" : "rshift",
4.22 + "Sub" : "sub",
4.23 +
4.24 + # Unary operations.
4.25 +
4.26 + "Invert" : "invert",
4.27 + "UnaryAdd" : "pos",
4.28 + "UnarySub" : "neg",
4.29 +
4.30 + # Augmented assignment.
4.31 +
4.32 + "+=" : "iadd",
4.33 + "-=" : "isub",
4.34 + "*=" : "imul",
4.35 + "/=" : "idiv",
4.36 + "//=" : "ifloordiv",
4.37 + "%=" : "imod",
4.38 + "**=" : "ipow",
4.39 + "<<=" : "ilshift",
4.40 + ">>=" : "irshift",
4.41 + "&=" : "iand",
4.42 + "^=" : "ixor",
4.43 + "|=" : "ior",
4.44 +
4.45 + # Comparisons.
4.46 +
4.47 + "==" : "eq",
4.48 + "!=" : "ne",
4.49 + "<" : "lt",
4.50 + "<=" : "le",
4.51 + ">=" : "ge",
4.52 + ">" : "gt"
4.53 + }
4.54 +
4.55 # vim: tabstop=4 expandtab shiftwidth=4
5.1 --- a/micropython/data.py Sat Aug 07 02:06:15 2010 +0200
5.2 +++ b/micropython/data.py Mon Aug 09 01:23:05 2010 +0200
5.3 @@ -906,7 +906,6 @@
5.4 self.name = name
5.5 self.parent = parent
5.6 self.astnode = node
5.7 - node._def = self
5.8
5.9 # Superclasses, descendants and attributes.
5.10
5.11 @@ -1285,7 +1284,6 @@
5.12 self.has_dstar = has_dstar
5.13 self.dynamic_def = dynamic_def
5.14 self.astnode = node
5.15 - node._def = self
5.16
5.17 # Initialise the positional names.
5.18
6.1 --- a/micropython/inspect.py Sat Aug 07 02:06:15 2010 +0200
6.2 +++ b/micropython/inspect.py Mon Aug 09 01:23:05 2010 +0200
6.3 @@ -408,6 +408,12 @@
6.4
6.5 # Generic support for classes of operations.
6.6
6.7 + def _ensureOperators(self):
6.8 + if not self.has_key("$operator"):
6.9 + module = self.importer.load("operator")
6.10 + self["$operator"] = module
6.11 + return self["$operator"].get_value()
6.12 +
6.13 def _visitUnary(self, node):
6.14
6.15 "Accounting method for the unary operator 'node'."
6.16 @@ -420,9 +426,9 @@
6.17
6.18 "Accounting method for the binary operator 'node'."
6.19
6.20 - left_method, right_method = binary_methods[node.__class__.__name__]
6.21 - self.use_name(left_method, node)
6.22 - self.use_name(right_method, node)
6.23 + operator_module = self._ensureOperators()
6.24 + operator_fn = operator_functions[node.__class__.__name__]
6.25 + self.use_specific_attribute(operator_module.full_name(), operator_fn)
6.26 return self.OP(node)
6.27
6.28 def _visitFunction(self, node, name):
6.29 @@ -565,10 +571,9 @@
6.30
6.31 # Accounting.
6.32
6.33 - aug_method, (left_method, right_method) = augassign_methods[node.op]
6.34 - self.use_name(aug_method, node)
6.35 - self.use_name(left_method, node)
6.36 - self.use_name(right_method, node)
6.37 + operator_fn = operator_functions.get(node.op)
6.38 + operator_module = self._ensureOperators()
6.39 + self.use_specific_attribute(operator_module.full_name(), operator_fn)
6.40
6.41 # Process the assignment.
6.42
6.43 @@ -676,15 +681,19 @@
6.44 for op in node.ops:
6.45 op_name, next_node = op
6.46
6.47 - # Get the applicable methods.
6.48 + # Define name/attribute usage.
6.49 + # Get the applicable operation.
6.50
6.51 - methods = comparison_methods[op_name]
6.52 + operator_fn = operator_functions.get(op_name)
6.53 +
6.54 + # For operators, reference the specific function involved.
6.55
6.56 - # Define name/attribute usage.
6.57 + if operator_fn is not None:
6.58 + operator_module = self._ensureOperators()
6.59 + self.use_specific_attribute(operator_module.full_name(), operator_fn)
6.60
6.61 - if methods is not None:
6.62 - self.use_name(methods[0], this_node)
6.63 - self.use_name(methods[1], next_node)
6.64 + # Define __contains__ usage on the next node.
6.65 +
6.66 elif op_name.endswith("in"):
6.67 self.use_name("__contains__", next_node)
6.68
7.1 --- a/micropython/report.py Sat Aug 07 02:06:15 2010 +0200
7.2 +++ b/micropython/report.py Mon Aug 09 01:23:05 2010 +0200
7.3 @@ -51,6 +51,7 @@
7.4 }
7.5
7.6 .nowrap { white-space: nowrap; }
7.7 + .label { font-size: smaller; }
7.8
7.9 .class { margin-top: 1em; margin-bottom: 1em; }
7.10 .function { margin-top: 1em; margin-bottom: 1em; }
7.11 @@ -67,36 +68,47 @@
7.12 position: absolute;
7.13 top: 3ex; left: 0;
7.14 color: white;
7.15 + border-bottom: 0.5ex solid #000;
7.16 + border-right: 0.5ex solid #000;
7.17 z-index: 3;
7.18 }
7.19
7.20 - .scope { padding: 0.5em; background-color: #007700; }
7.21 -
7.22 + .assname,
7.23 .name {
7.24 position: relative;
7.25 + background-color: #300;
7.26 color: white;
7.27 }
7.28
7.29 + .assname:hover,
7.30 .name:hover {
7.31 - background-color: #003300;
7.32 - padding-bottom: 1ex;
7.33 + background-color: #500;
7.34 + padding-top: 0.5ex;
7.35 + padding-bottom: 0.5ex;
7.36 z-index: 2;
7.37 }
7.38
7.39 + .assname:hover .popup,
7.40 .name:hover .popup {
7.41 display: block;
7.42 }
7.43
7.44 + .attrnames,
7.45 + .scope {
7.46 + padding: 0.5em;
7.47 + background-color: #700;
7.48 + }
7.49 +
7.50 .summary-class {
7.51 - background-color: #004400;
7.52 + background-color: #040;
7.53 }
7.54
7.55 .summary-instance {
7.56 - background-color: #0000FF;
7.57 + background-color: #00F;
7.58 }
7.59
7.60 .summary-attr {
7.61 - background-color: #007700;
7.62 + background-color: #070;
7.63 }
7.64
7.65 </style>
7.66 @@ -188,7 +200,34 @@
7.67 _popup_end = _span_end
7.68
7.69 def _scope(self, scope):
7.70 - self.stream.write("<div class='scope'>%s</div>\n" % scope)
7.71 + self.stream.write("<div class='scope'><span class='label'>scope</span><br />%s</div>\n" % scope)
7.72 +
7.73 + def _assname(self, name, node):
7.74 + self.stream.write("<span class='assname'>")
7.75 + if hasattr(node, "_attrnames"):
7.76 + attrnames = node._attrnames
7.77 + self._name_start()
7.78 + self.stream.write(name)
7.79 + self._popup_start()
7.80 + self._attrnames(attrnames[name])
7.81 + self._popup_end()
7.82 + self._name_end()
7.83 + else:
7.84 + self._name(node.name)
7.85 + self.stream.write("</span>")
7.86 +
7.87 + def _attrnames(self, attrnames):
7.88 + if not attrnames:
7.89 + return
7.90 +
7.91 + self.stream.write("<div class='attrnames'><span class='label'>attributes</span><br />")
7.92 + first = 1
7.93 + for attrname in attrnames:
7.94 + if not first:
7.95 + self.stream.write("<br />")
7.96 + self.stream.write(attrname)
7.97 + first = 0
7.98 + self.stream.write("</div>\n")
7.99
7.100 # Summary classes.
7.101
7.102 @@ -322,8 +361,8 @@
7.103
7.104 # Use inspected details where possible.
7.105
7.106 - if hasattr(node, "_def"):
7.107 - cls = node._def
7.108 + if hasattr(node, "unit"):
7.109 + cls = node.unit
7.110 bases = cls.bases
7.111 self.stream.write("<div class='class nowrap' id='%s'>\n" % cls.full_name())
7.112 else:
7.113 @@ -413,8 +452,8 @@
7.114 if not used_by_unit(node):
7.115 return
7.116
7.117 - if hasattr(node, "_def"):
7.118 - fn = node._def
7.119 + if hasattr(node, "unit"):
7.120 + fn = node.unit
7.121 self.stream.write("<div class='function nowrap' id='%s'>\n" % fn.full_name())
7.122 else:
7.123 print "Warning: function %s not recognised!" % node.name
7.124 @@ -427,7 +466,7 @@
7.125 self._object_name_def(self.module, fn, "function-name")
7.126
7.127 self.stream.write("(")
7.128 - self._parameters(fn)
7.129 + self._parameters(fn, node)
7.130 self.stream.write(")")
7.131 self.stream.write(":\n")
7.132 self.stream.write("</div>\n")
7.133 @@ -669,7 +708,7 @@
7.134 self.stream.write("</span>")
7.135
7.136 def visitAssName(self, node):
7.137 - self._name(node.name)
7.138 + self._assname(node.name, node)
7.139
7.140 def visitAssTuple(self, node):
7.141 self.stream.write("<span class='tuple'>")
7.142 @@ -751,15 +790,15 @@
7.143 self.stream.write("</span>")
7.144
7.145 def visitLambda(self, node):
7.146 - if hasattr(node, "_def"):
7.147 - fn = node._def
7.148 + if hasattr(node, "unit"):
7.149 + fn = node.unit
7.150 else:
7.151 print "Warning: function %s not recognised!" % node.name
7.152 return
7.153
7.154 self.stream.write("<span class='lambda'>")
7.155 self._keyword("lambda")
7.156 - self._parameters(fn)
7.157 + self._parameters(fn, node)
7.158 self.stream.write(": <span class='code'>")
7.159 self.dispatch(node.code)
7.160 self.stream.write("</span>")
7.161 @@ -836,7 +875,7 @@
7.162 self.dispatch(v)
7.163 first = 0
7.164
7.165 - def _parameters(self, fn):
7.166 + def _parameters(self, fn, node):
7.167 nparams = len(fn.positional_names)
7.168 ndefaults = len(fn.defaults)
7.169 first_with_default = nparams - ndefaults
7.170 @@ -845,7 +884,7 @@
7.171 for n, param in enumerate(fn.positional_names):
7.172 if not first:
7.173 self.stream.write(", ")
7.174 - self._name(param)
7.175 + self._assname(param, node)
7.176 n_default = n - first_with_default
7.177 if n_default >= 0:
7.178 self._default(fn.defaults[n_default])
8.1 --- a/micropython/trans.py Sat Aug 07 02:06:15 2010 +0200
8.2 +++ b/micropython/trans.py Mon Aug 09 01:23:05 2010 +0200
8.3 @@ -244,6 +244,12 @@
8.4 on the 'extend' instruction.
8.5 """
8.6
8.7 + # Remove any ExtendFrame instructions which do nothing.
8.8 +
8.9 + if self.last_op() is extend:
8.10 + self.remove_op()
8.11 + return
8.12 +
8.13 ntemp = self.max_temp_position + 1
8.14 extend.attr = ntemp + node.unit.local_usage # NOTE: See get_code for similar code.
8.15
8.16 @@ -1055,12 +1061,12 @@
8.17
8.18 self.new_op(CheckFrame((nparams, ndefaults)))
8.19
8.20 - if fn.is_dynamic():
8.21 - self.new_op(LoadTemp(0)) # context provides storage
8.22 - else:
8.23 - self.new_op(LoadFunction(fn))
8.24 + if ndefaults > 0:
8.25 + if fn.is_dynamic():
8.26 + self.new_op(LoadTemp(0)) # context provides storage
8.27 + else:
8.28 + self.new_op(LoadFunction(fn))
8.29
8.30 - if ndefaults > 0:
8.31 self.new_op(FillDefaults((nparams, ndefaults)))
8.32
8.33 # Produce the body.
8.34 @@ -1208,78 +1214,32 @@
8.35 def _visitUnary(self, node):
8.36
8.37 """
8.38 - _t = node.expr
8.39 - try:
8.40 - _result = _t.__pos__()
8.41 - except AttributeError:
8.42 - raise TypeError
8.43 + Invoke the appropriate operator module function for the operation
8.44 + represented by 'node'.
8.45 """
8.46
8.47 - method = unary_methods[node.__class__.__name__]
8.48 + temp_fn = self._getOperatorFunction(node)
8.49 + self._visitUnaryCall(node, temp_fn, node.expr)
8.50 + self.discard_temp(temp_fn)
8.51 +
8.52 + def _visitUnaryCall(self, node, temp_fn, expr):
8.53
8.54 - handler_block = self.new_block()
8.55 - else_block = self.new_block()
8.56 - end_block = self.new_block()
8.57 + """
8.58 + Invoke the appropriate operator module function for the operation
8.59 + represented by 'node', given a 'temp_fn' reference to a function, along
8.60 + with the 'expr' operand node.
8.61 + """
8.62
8.63 # Evaluate and store the operand in temporary storage.
8.64
8.65 - self.dispatch(node.expr)
8.66 + self.dispatch(expr)
8.67 temp = self.optimiser.optimise_temp_storage()
8.68
8.69 - self.new_op(temp)
8.70 -
8.71 - # Try to get the attribute, handling exceptions.
8.72 -
8.73 - self.new_op(PushHandler(handler_block))
8.74 -
8.75 - # Get the method on temp.
8.76 -
8.77 - self._generateAttr(node, method, self.attribute_load_instructions)
8.78 - temp_method = self.optimiser.optimise_temp_storage()
8.79 -
8.80 - # Finish handling any attribute access exceptions.
8.81 -
8.82 - have_handler = self.new_op(PopHandler())
8.83 -
8.84 - # Add arguments.
8.85 - # NOTE: No support for defaults.
8.86 -
8.87 - self._startCallFunc()
8.88 - self.new_op(temp) # Explicit context as first argument.
8.89 - self.new_op(StoreFrame(0))
8.90 - self._endCallFuncArgs(1)
8.91 - self._doCallFunc(temp_method)
8.92 - self._endCallFunc(temp_method)
8.93 -
8.94 - # Store the result.
8.95 -
8.96 - temp_out = self.get_temp()
8.97 - self.new_op(Jump(end_block))
8.98 -
8.99 - # End method attempt.
8.100 -
8.101 - self.set_block(handler_block)
8.102 -
8.103 - if have_handler:
8.104 - self.new_op(PopHandler())
8.105 - self._handleAttributeError(node, temp_method, else_block)
8.106 -
8.107 - # Raise a TypeError.
8.108 -
8.109 - self.set_block(else_block)
8.110 - self.make_exception("TypeError", node)
8.111 - self.new_op(StoreException())
8.112 - self.new_op(RaiseException())
8.113 -
8.114 - # Produce the result.
8.115 -
8.116 - self.set_block(end_block)
8.117 - self.new_op(temp_out)
8.118 + self._generateInvocation(temp_fn, (temp,))
8.119
8.120 # Compilation duties...
8.121
8.122 self.discard_temp(temp)
8.123 - self.discard_temp(temp_out)
8.124
8.125 def _visitBinaryBit(self, node):
8.126
8.127 @@ -1289,36 +1249,34 @@
8.128 bitwise operators.
8.129 """
8.130
8.131 + temp_fn = self._getOperatorFunction(node)
8.132 left = None
8.133
8.134 for right in node.nodes:
8.135 if left is not None:
8.136 - self._visitBinaryMethods(node, left, right)
8.137 + self._visitBinaryCall(node, temp_fn, left, right)
8.138 left = right
8.139
8.140 + self.discard_temp(temp_fn)
8.141 +
8.142 def _visitBinary(self, node):
8.143
8.144 """
8.145 - _t1 = node.left
8.146 - _t2 = node.right
8.147 - try:
8.148 - _result = _t1.__add__(_t2)
8.149 - if _result is NotImplemented:
8.150 - raise AttributeError
8.151 - except AttributeError:
8.152 - try:
8.153 - _result = _t2.__radd__(_t1)
8.154 - if _result is NotImplemented:
8.155 - raise AttributeError
8.156 - except AttributeError:
8.157 - raise TypeError
8.158 + Invoke the appropriate operator module function for the operation
8.159 + represented by 'node'.
8.160 """
8.161
8.162 - self._visitBinaryMethods(node, node.left, node.right)
8.163 + temp_fn = self._getOperatorFunction(node)
8.164 + self._visitBinaryCall(node, temp_fn, node.left, node.right)
8.165 + self.discard_temp(temp_fn)
8.166 +
8.167 + def _visitBinaryCall(self, node, temp_fn, left, right):
8.168
8.169 - def _visitBinaryMethods(self, node, left, right):
8.170 -
8.171 - left_method, right_method = binary_methods[node.__class__.__name__]
8.172 + """
8.173 + Invoke the appropriate operator module function for the operation
8.174 + represented by 'node', given a 'temp_fn' reference to a function, along
8.175 + with the 'left' and 'right' operand nodes.
8.176 + """
8.177
8.178 # Evaluate and store the left operand in temporary storage.
8.179
8.180 @@ -1330,117 +1288,59 @@
8.181 self.dispatch(right)
8.182 temp2 = self.optimiser.optimise_temp_storage()
8.183
8.184 - temp_out = self._generateBinary(node, temp1, temp2, left_method, right_method)
8.185 -
8.186 - # Produce the result.
8.187 -
8.188 - self.new_op(temp_out)
8.189 + self._generateInvocation(temp_fn, (temp1, temp2))
8.190
8.191 # Compilation duties...
8.192
8.193 self.discard_temp(temp1)
8.194 self.discard_temp(temp2)
8.195 - self.discard_temp(temp_out)
8.196
8.197 - def _generateBinary(self, node, temp1, temp2, left_method, right_method):
8.198 -
8.199 - """
8.200 - For the given 'node', generate the binary operator pattern for the
8.201 - operands 'temp1' and 'temp2', employing 'left_method' and 'right_method'
8.202 - as defined for binary operators, but also used in comparisons (for which
8.203 - this method is provided).
8.204 -
8.205 - A temporary storage reference is returned from this method.
8.206 - """
8.207 -
8.208 - right_block = self.new_block()
8.209 - left_else_block = self.new_block()
8.210 - right_else_block = self.new_block()
8.211 - end_block = self.new_block()
8.212 -
8.213 - # Left method.
8.214 -
8.215 - temp_out = self._generateOpMethod(node, temp1, temp2, left_method, left_else_block, right_block, end_block)
8.216 - self.discard_temp(temp_out) # NOTE: Will re-use the same storage.
8.217 -
8.218 - self.set_block(left_else_block)
8.219 - self.new_op(ClearException())
8.220 -
8.221 - # Right method.
8.222 -
8.223 - self.set_block(right_block)
8.224 - temp_out = self._generateOpMethod(node, temp2, temp1, right_method, right_else_block, right_else_block, end_block)
8.225 -
8.226 - # Raise a TypeError.
8.227 -
8.228 - self.set_block(right_else_block)
8.229 - self.make_exception("TypeError", node)
8.230 - self.new_op(StoreException())
8.231 - self.new_op(RaiseException())
8.232 -
8.233 - self.set_block(end_block)
8.234 - return temp_out
8.235 -
8.236 - def _generateOpMethod(self, node, temp1, temp2, method_name, handled_block, next_method_block, end_block):
8.237 + def _generateInvocation(self, temp_fn, temp_list):
8.238
8.239 """
8.240 - For the given 'node', generate the operator method invocation using the
8.241 - operands 'temp1' and 'temp2', employing the given 'method_name', and
8.242 - jumping appropriately to 'handled_block' where an AttributeError was
8.243 - handled, to 'next_method_block' where a NotImplemented result is
8.244 - returned, or to 'end_block' if the method call was successful.
8.245 -
8.246 - A temporary storage reference is returned from this method.
8.247 + Invoke the function 'temp_fn' using the operands from 'temp_list' as
8.248 + arguments.
8.249 """
8.250
8.251 - handler_block = self.new_block()
8.252 -
8.253 - # Try to get the attribute, handling exceptions.
8.254 -
8.255 - self.new_op(PushHandler(handler_block))
8.256 - self.new_op(temp1)
8.257 -
8.258 - # Get method on temp1.
8.259 -
8.260 - self._generateAttr(node, method_name, self.attribute_load_instructions)
8.261 - temp_method = self.optimiser.optimise_temp_storage()
8.262 -
8.263 - # Finish handling any attribute access exceptions.
8.264 -
8.265 - have_handler = self.new_op(PopHandler())
8.266 -
8.267 - # Add arguments.
8.268 - # NOTE: No support for defaults.
8.269 -
8.270 self._startCallFunc()
8.271 - self.new_op(temp1)
8.272 - self.new_op(StoreFrame(0))
8.273 - self.new_op(temp2)
8.274 - self.new_op(StoreFrame(1))
8.275 - self._endCallFuncArgs(2)
8.276 - self._doCallFunc(temp_method)
8.277 - self._endCallFunc(temp_method)
8.278 +
8.279 + for i, temp in enumerate(temp_list):
8.280 + self.new_op(temp)
8.281 + self.new_op(StoreFrame(i))
8.282
8.283 - # Store the result.
8.284 + self._endCallFuncArgs(len(temp_list))
8.285 + self._doCallFunc(temp_fn)
8.286 + self._endCallFunc(temp_fn)
8.287 +
8.288 + def _getOperatorFunction(self, node):
8.289
8.290 - temp_out = self.get_temp()
8.291 + "Return an operator function reference for the given 'node'."
8.292 +
8.293 + return self._generateOperatorFunction(node.__class__.__name__)
8.294 +
8.295 + def _getOperatorAugAssignFunction(self, node):
8.296
8.297 - # Test for NotImplemented.
8.298 - # Don't actually raise an exception.
8.299 + """
8.300 + Return an operator augmented assignment function reference for the given
8.301 + 'node'.
8.302 + """
8.303
8.304 - self.new_op(TestIdentityAddress(self.importer.get_predefined_constant("NotImplemented")))
8.305 - self.new_op(JumpIfTrue(next_method_block))
8.306 - self.new_op(Jump(end_block))
8.307 + return self._generateOperatorFunction(node.op)
8.308 +
8.309 + def _generateOperatorFunction(self, opname):
8.310 +
8.311 + "Return an operator function reference for the given 'opname'."
8.312
8.313 - # End method attempt.
8.314 + operator_fn = operator_functions[opname]
8.315
8.316 - self.set_block(handler_block)
8.317 + # Get the operator module.
8.318
8.319 - if have_handler:
8.320 - self.new_op(PopHandler())
8.321 - self._handleAttributeError(node, temp_method, handled_block)
8.322 + operator_module = self.importer.get_module("operator")
8.323
8.324 - return temp_out
8.325 + # Get the appropriate function from the operator module.
8.326 +
8.327 + self.new_op(LoadAddress(operator_module[operator_fn]))
8.328 + return self.optimiser.optimise_temp_storage()
8.329
8.330 def _handleAttributeError(self, node, temp_method, handled_block):
8.331
8.332 @@ -1515,12 +1415,7 @@
8.333 self._generateAttr(node, "__bool__", self.attribute_load_instructions)
8.334 temp_method = self.optimiser.optimise_temp_storage()
8.335
8.336 - self._startCallFunc()
8.337 - self.new_op(temp)
8.338 - self.new_op(StoreFrame(0))
8.339 - self._endCallFuncArgs(1)
8.340 - self._doCallFunc(temp_method)
8.341 - self._endCallFunc(temp_method)
8.342 + self._generateInvocation(temp_method, (temp,))
8.343
8.344 self.discard_temp(temp_method)
8.345
9.1 --- a/rsvp.py Sat Aug 07 02:06:15 2010 +0200
9.2 +++ b/rsvp.py Mon Aug 09 01:23:05 2010 +0200
9.3 @@ -840,10 +840,11 @@
9.4 rc = program.get_raw_image()
9.5 print "Initialising the machine..."
9.6 importer = program.get_importer()
9.7 - true_constant = importer.get_constant(True).location
9.8 - false_constant = importer.get_constant(False).location
9.9 + constants = {}
9.10 + for x in (True, False, NotImplemented):
9.11 + constants[x] = importer.get_constant(x).location
9.12 rm = RSVPMachine(rc, objlist, paramlist, debug=debug, abort_upon_exception=abort_upon_exception)
9.13 - library = Library(rm, true_constant, false_constant)
9.14 + library = Library(rm, constants)
9.15 rm.library = library
9.16 rm.pc = program.code_location
9.17 print "Returning program occupying %d locations." % len(rm.memory)
10.1 --- a/rsvplib.py Sat Aug 07 02:06:15 2010 +0200
10.2 +++ b/rsvplib.py Mon Aug 09 01:23:05 2010 +0200
10.3 @@ -3,7 +3,7 @@
10.4 """
10.5 A native function library for a really simple virtual processor.
10.6
10.7 -Copyright (C) 2007, 2008, 2009 Paul Boddie <paul@boddie.org.uk>
10.8 +Copyright (C) 2007, 2008, 2009, 2010 Paul Boddie <paul@boddie.org.uk>
10.9
10.10 This program is free software; you can redistribute it and/or modify it under
10.11 the terms of the GNU General Public License as published by the Free Software
10.12 @@ -26,16 +26,15 @@
10.13
10.14 "Native function implementations."
10.15
10.16 - def __init__(self, machine, true_constant, false_constant):
10.17 + def __init__(self, machine, constants):
10.18
10.19 """
10.20 - Initialise the library with the 'machine' and the addresses
10.21 - 'true_constant' and 'false_constant'.
10.22 + Initialise the library with the 'machine' and the 'constants' addresses
10.23 + dictionary.
10.24 """
10.25
10.26 self.machine = machine
10.27 - self.true_constant = true_constant
10.28 - self.false_constant = false_constant
10.29 + self.constants = constants
10.30
10.31 # Native class constants.
10.32
10.33 @@ -97,7 +96,7 @@
10.34
10.35 self.machine.result = DataValue(addr, addr)
10.36
10.37 - def builtins_logical_op(self, operand_class, op, true_if_incompatible):
10.38 + def builtins_logical_op(self, operand_class, op):
10.39 frame = self.local_sp_stack[-1]
10.40
10.41 # Get operands addresses.
10.42 @@ -107,14 +106,13 @@
10.43
10.44 # Test operand suitability.
10.45 # NOTE: Handle comparisons of incompatible types more appropriately.
10.46 + # NOTE: Return NotImplemented.
10.47
10.48 if not (self.machine._CheckInstance(left_value.ref, operand_class) and
10.49 self.machine._CheckInstance(right_value.ref, operand_class)):
10.50
10.51 - if true_if_incompatible:
10.52 - self.machine.result = DataValue(self.true_constant, self.true_constant)
10.53 - else:
10.54 - self.machine.result = DataValue(self.false_constant, self.false_constant)
10.55 + notimpl = self.constants[NotImplemented]
10.56 + self.machine.result = DataValue(notimpl, notimpl)
10.57 return
10.58
10.59 # NOTE: Assume single location for data.
10.60 @@ -126,9 +124,9 @@
10.61 # NOTE: The data is considered ready to use.
10.62
10.63 if op(self.machine.load(left_data), self.machine.load(right_data)):
10.64 - self.machine.result = DataValue(self.true_constant, self.true_constant)
10.65 + self.machine.result = DataValue(self.constants[True], self.constants[True])
10.66 else:
10.67 - self.machine.result = DataValue(self.false_constant, self.false_constant)
10.68 + self.machine.result = DataValue(self.constants[False], self.constants[False])
10.69
10.70 # Operators.
10.71 # Although this takes a short-cut by using the operator module, testing is
10.72 @@ -145,40 +143,40 @@
10.73 return self.builtins_int_arithmetic_op(operator.pow)
10.74
10.75 def builtins_int_lt(self):
10.76 - return self.builtins_logical_op(self.int_class, operator.lt, 0)
10.77 + return self.builtins_logical_op(self.int_class, operator.lt)
10.78
10.79 def builtins_int_le(self):
10.80 - return self.builtins_logical_op(self.int_class, operator.le, 0)
10.81 + return self.builtins_logical_op(self.int_class, operator.le)
10.82
10.83 def builtins_int_gt(self):
10.84 - return self.builtins_logical_op(self.int_class, operator.gt, 0)
10.85 + return self.builtins_logical_op(self.int_class, operator.gt)
10.86
10.87 def builtins_int_ge(self):
10.88 - return self.builtins_logical_op(self.int_class, operator.ge, 0)
10.89 + return self.builtins_logical_op(self.int_class, operator.ge)
10.90
10.91 def builtins_int_eq(self):
10.92 - return self.builtins_logical_op(self.int_class, operator.eq, 0)
10.93 + return self.builtins_logical_op(self.int_class, operator.eq)
10.94
10.95 def builtins_int_ne(self):
10.96 - return self.builtins_logical_op(self.int_class, operator.ne, 1)
10.97 + return self.builtins_logical_op(self.int_class, operator.ne)
10.98
10.99 def builtins_str_lt(self):
10.100 - return self.builtins_logical_op(self.str_class, operator.lt, 0)
10.101 + return self.builtins_logical_op(self.str_class, operator.lt)
10.102
10.103 def builtins_str_le(self):
10.104 - return self.builtins_logical_op(self.str_class, operator.le, 0)
10.105 + return self.builtins_logical_op(self.str_class, operator.le)
10.106
10.107 def builtins_str_gt(self):
10.108 - return self.builtins_logical_op(self.str_class, operator.gt, 0)
10.109 + return self.builtins_logical_op(self.str_class, operator.gt)
10.110
10.111 def builtins_str_ge(self):
10.112 - return self.builtins_logical_op(self.str_class, operator.ge, 0)
10.113 + return self.builtins_logical_op(self.str_class, operator.ge)
10.114
10.115 def builtins_str_eq(self):
10.116 - return self.builtins_logical_op(self.str_class, operator.eq, 0)
10.117 + return self.builtins_logical_op(self.str_class, operator.eq)
10.118
10.119 def builtins_str_ne(self):
10.120 - return self.builtins_logical_op(self.str_class, operator.ne, 1)
10.121 + return self.builtins_logical_op(self.str_class, operator.ne)
10.122
10.123 def builtins_int_and(self):
10.124 return self.builtins_int_arithmetic_op(operator.and_)
10.125 @@ -209,9 +207,9 @@
10.126 # NOTE: The data is considered ready to use.
10.127
10.128 if self.machine.load(left_data) != 0:
10.129 - self.machine.result = DataValue(self.true_constant, self.true_constant)
10.130 + self.machine.result = DataValue(self.constants[True], self.constants[True])
10.131 else:
10.132 - self.machine.result = DataValue(self.false_constant, self.false_constant)
10.133 + self.machine.result = DataValue(self.constants[False], self.constants[False])
10.134
10.135 def builtins_int_neg(self):
10.136 frame = self.local_sp_stack[-1]