# HG changeset patch # User Paul Boddie # Date 1288570964 -3600 # Node ID ecfc0f945064b30cf381b12da2d3615ce79cc06b # Parent 078c71c2a581c816a556d728e9af674d602d3f88 Simplified the RSVP library implementations for item access, introducing Python code for __getitem__ and __getslice__ along with various utility functions. diff -r 078c71c2a581 -r ecfc0f945064 TO_DO.txt --- a/TO_DO.txt Mon Sep 27 23:30:55 2010 +0200 +++ b/TO_DO.txt Mon Nov 01 01:22:44 2010 +0100 @@ -1,10 +1,5 @@ -Support slicing. This is difficult because __getitem__ has to handle integers and slice -objects differently. One could either just try and iterate over the argument and then -catch the AttributeError for integers, or one could test the instances first. - -Support isinstance. Like slicing, the problem is dealing with the class or tuple input -to the function. A strict tuple check is permissible according to the CPython behaviour, -but an iterable would be more elegant (as would *args). +Consider attribute assignment observations, along with the possibility of class attribute +assignment. Local assignment detection plus frame re-use. Example: slice.__init__ calls xrange.__init__ with the same arguments which are unchanged in xrange.__init__. There is diff -r 078c71c2a581 -r ecfc0f945064 lib/builtins.py --- a/lib/builtins.py Mon Sep 27 23:30:55 2010 +0200 +++ b/lib/builtins.py Mon Nov 01 01:22:44 2010 +0100 @@ -173,12 +173,20 @@ self._elements = None def __getitem__(self, index): - # Note usage. - IndexError + + "Return the item or slice specified by 'index'." + + return _getitem(self, index) def __contains__(self, value): pass def __setitem__(self, index, value): pass - def __getslice__(self, start, end=None): pass + + def __getslice__(self, start, end=None): + + "Return a slice starting from 'start', with the optional 'end'." + + return _getslice(self, start, end) + def __setslice__(self, start, end, slice): pass def append(self, value): pass @@ -202,6 +210,10 @@ return listiterator(self) + # Special implementation methods. + + def __get_single_item__(self, index): pass + class listiterator(object): "Implementation of listiterator." @@ -270,10 +282,17 @@ def __init__(self, args): pass def __getitem__(self, index): - # Note usage. - IndexError + + "Return the item or slice specified by 'index'." + + return _getitem(self, index) - def __getslice__(self, start, end=None): pass + def __getslice__(self, start, end=None): + + "Return a slice starting from 'start', with the optional 'end'." + + return tuple(_getslice(self, start, end)) + def __len__(self): pass def __add__(self, other): pass def __str__(self): pass @@ -285,6 +304,10 @@ return listiterator(self) + # Special implementation methods. + + def __get_single_item__(self, index): pass + class unicode(basestring): pass @@ -435,7 +458,10 @@ either a class or a tuple of classes. """ + # NOTE: CPython insists on tuples, but any sequence might be considered + # NOTE: acceptable. # NOTE: tuple.__class__ is tuple in micropython! + if cls_or_tuple is not tuple and cls_or_tuple.__class__ is tuple: for cls in cls_or_tuple: if _isinstance(obj, cls): @@ -444,8 +470,6 @@ else: return _isinstance(obj, cls_or_tuple) -def _isinstance(obj, cls): pass - def issubclass(obj, cls_or_tuple): pass def iter(collection): @@ -511,6 +535,81 @@ def vars(obj=None): pass def zip(*args): pass +# Utility functions. + +def _get_absolute_index(index, length): + + """ + Return the absolute index for 'index' given a collection having the + specified 'length'. + """ + + if index < 0: + return length + index + else: + return index + +def _normalise_index(index, length): + + "Normalise 'index' for a collection having the specified 'length'." + + return _min(length, _max(0, _get_absolute_index(index, length))) + +def _max(x, y): + + "Return the maximum of 'x' and 'y'." + + if x >= y: + return x + else: + return y + +def _min(x, y): + + "Return the minimum of 'x' and 'y'." + + if x <= y: + return x + else: + return y + +def _getitem(seq, index): + + "Return the item or slice specified by 'index'." + + if isinstance(index, int): + return seq.__get_single_item__(index) + elif isinstance(index, slice): + return seq.__getslice__(index.start, index.end) + else: + raise TypeError + +def _getslice(seq, start, end=None): + + "Return a slice starting from 'start', with the optional 'end'." + + length = len(seq) + + if start is None: + start = 0 + else: + start = _normalise_index(start, length) + + if end is None: + end = length + else: + end = _normalise_index(end, length) + + result = [] + while start < end: + result.append(seq.__get_single_item__(start)) + start += 1 + return result + +# Special implementation functions. + +def _isinstance(obj, cls): pass + # Reference some names to ensure their existence. This should be everything # mentioned in a get_builtin or load_builtin call. Instances from this module # should be predefined constants. diff -r 078c71c2a581 -r ecfc0f945064 rsvplib.py --- a/rsvplib.py Mon Sep 27 23:30:55 2010 +0200 +++ b/rsvplib.py Mon Nov 01 01:22:44 2010 +0100 @@ -60,6 +60,14 @@ self.frame_stack = self.machine.frame_stack self.local_sp_stack = self.machine.local_sp_stack + def _get_absolute_index(self, pos, nelements): + if pos >= 0 and pos < nelements: + return pos + elif pos < 0 and pos >= -nelements: + return nelements + pos + else: + return None + def builtins_int_arithmetic_op(self, op): frame = self.local_sp_stack[-1] @@ -270,7 +278,7 @@ addr = list_value.ref + 1 self.machine.save(addr, DataValue(None, new_fragment)) - def builtins_list_getitem(self): + def builtins_list_get_single_item(self): frame = self.local_sp_stack[-1] # Get the operand address. @@ -291,24 +299,14 @@ header = self.machine.load(fragment.ref) nelements = header.occupied_size - 1 - # Test operand suitability. - - if self.machine._CheckInstance(item_value.ref, self.int_class): + # NOTE: Assume single location for data and header. - # NOTE: Assume single location for data and header. - - item_pos = self.machine.load(item_value.ref + 1) + item_pos = self.machine.load(item_value.ref + 1) - if item_pos >= 0 and item_pos < nelements: - pass - elif item_pos < 0 and item_pos >= -nelements: - item_pos = nelements + item_pos - else: - self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) - return self.machine.RaiseException() + item_pos = self._get_absolute_index(item_pos, nelements) - else: - self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) + if item_pos is None: + self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) return self.machine.RaiseException() # NOTE: Assume single location for header. @@ -417,7 +415,7 @@ self.machine.result = DataValue(addr, addr) - def builtins_tuple_getitem(self): + def builtins_tuple_get_single_item(self): frame = self.local_sp_stack[-1] # Get the operand address. @@ -438,11 +436,9 @@ item_pos = self.machine.load(item_value.ref + 1) - if item_pos >= 0 and item_pos < nelements: - pass - elif item_pos < 0 and item_pos >= -nelements: - item_pos = nelements + item_pos - else: + item_pos = self._get_absolute_index(item_pos, nelements) + + if item_pos is None: self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) return self.machine.RaiseException() @@ -488,11 +484,11 @@ "__builtins__.int.__or__" : builtins_int_or, "__builtins__.int.__ror__" : builtins_int_or, "__builtins__.bool.__bool__" : builtins_bool_bool, - "__builtins__.list.__getitem__" : builtins_list_getitem, + "__builtins__.list.__get_single_item__" : builtins_list_get_single_item, "__builtins__.list.__len__" : builtins_list_len, "__builtins__.list.append" : builtins_list_append, "__builtins__.tuple.__len__" : builtins_tuple_len, - "__builtins__.tuple.__getitem__" : builtins_tuple_getitem, + "__builtins__.tuple.__get_single_item__" : builtins_tuple_get_single_item, "__builtins__.basestring.__lt__" : builtins_str_lt, "__builtins__.basestring.__le__" : builtins_str_le, "__builtins__.basestring.__gt__" : builtins_str_gt,