# HG changeset patch # User Paul Boddie # Date 1481558759 -3600 # Node ID f42e303e5dc521469fbef4c6ef14b1629aec49cd # Parent 42fed2e5be00f31df56c10faaac78f2eebde4ba7 Fixed string slicing to produce strings, changing sequences to not provide list-based slicing to all descendant classes. Changed the native substring function to use start, end and step information, adding step support. Changed the names in the native string module to reflect __data__ attribute usage. Added tests of string slicing. diff -r 42fed2e5be00 -r f42e303e5dc5 lib/__builtins__/sequence.py --- a/lib/__builtins__/sequence.py Mon Dec 12 01:46:28 2016 +0100 +++ b/lib/__builtins__/sequence.py Mon Dec 12 17:05:59 2016 +0100 @@ -35,6 +35,16 @@ if index < 0 or index >= len(self): raise IndexError(index) + def _check_end_index(self, index): + + """ + Check the given absolute end 'index', raising an IndexError if out of + bounds. + """ + + if index < -1 or index > len(self): + raise IndexError(index) + def __getitem__(self, index): "Return the item or slice specified by 'index'." @@ -112,13 +122,7 @@ else: end = _get_absolute_index(end, length) - result = [] - - while step > 0 and start < end or step < 0 and start > end: - result.append(self.__get_single_item__(start)) - start += step - - return result + return self.__get_multiple_items__(start, end, step) # Methods implemented by subclasses. @@ -140,6 +144,12 @@ pass + def __get_multiple_items__(self, start, end, step): + + "Method to be overridden by subclasses." + + return None + def __len__(self): "Method to be overridden by subclasses." @@ -230,6 +240,21 @@ raise StopIteration() + def __get_multiple_items__(self, start, end, step): + + """ + Return items from 'start' until (but excluding) 'end', at 'step' + intervals. + """ + + result = [] + + while step > 0 and start < end or step < 0 and start > end: + result.append(self.__get_single_item__(start)) + start += step + + return result + def _get_absolute_index(index, length): """ diff -r 42fed2e5be00 -r f42e303e5dc5 lib/__builtins__/str.py --- a/lib/__builtins__/str.py Mon Dec 12 01:46:28 2016 +0100 +++ b/lib/__builtins__/str.py Mon Dec 12 17:05:59 2016 +0100 @@ -22,6 +22,7 @@ from __builtins__.int import maxint, minint from __builtins__.operator import _negate from __builtins__.sequence import itemaccess +from __builtins__.types import check_int from native import str_add, str_lt, str_gt, str_eq, str_len, str_nonempty, \ str_substr @@ -189,7 +190,26 @@ "Return the item at the normalised (positive) 'index'." self._check_index(index) - return str_substr(self.__data__, index, 1) + return str_substr(self.__data__, index, index + 1, 1) + + def __get_multiple_items__(self, start, end, step): + + """ + Return items from 'start' until (but excluding) 'end', at 'step' + intervals. + """ + + self._check_index(start) + self._check_end_index(end) + check_int(step) + + if step == 0: + raise ValueError(step) + + if start == end: + return "" + + return str_substr(self.__data__, start, end, step) class string(basestring): pass diff -r 42fed2e5be00 -r f42e303e5dc5 lib/native/str.py --- a/lib/native/str.py Mon Dec 12 01:46:28 2016 +0100 +++ b/lib/native/str.py Mon Dec 12 17:05:59 2016 +0100 @@ -26,13 +26,13 @@ # String operations. -def str_add(self, other): pass -def str_eq(self, other): pass -def str_gt(self, other): pass -def str_lt(self, other): pass -def str_len(self): pass -def str_nonempty(self): pass -def str_ord(self): pass -def str_substr(self, start, size): pass +def str_add(data, other_data): pass +def str_eq(data, other_data): pass +def str_gt(data, other_data): pass +def str_lt(data, other_data): pass +def str_len(data): pass +def str_nonempty(data): pass +def str_ord(data): pass +def str_substr(data, start, end, step): pass # vim: tabstop=4 expandtab shiftwidth=4 diff -r 42fed2e5be00 -r f42e303e5dc5 templates/native/str.c --- a/templates/native/str.c Mon Dec 12 01:46:28 2016 +0100 +++ b/templates/native/str.c Mon Dec 12 17:05:59 2016 +0100 @@ -113,18 +113,33 @@ { __attr * const _data = &__args[1]; __attr * const start = &__args[2]; - __attr * const size = &__args[3]; + __attr * const end = &__args[3]; + __attr * const step = &__args[4]; /* _data interpreted as string */ char *s = _data->strvalue, *sub; /* start.__data__ interpreted as int */ - int i = __load_via_object(start->value, __pos___data__).intvalue; - /* size.__data__ interpreted as int */ - int l = __load_via_object(size->value, __pos___data__).intvalue; + int istart = __load_via_object(start->value, __pos___data__).intvalue; + /* end.__data__ interpreted as int */ + int iend = __load_via_object(end->value, __pos___data__).intvalue; + /* step.__data__ interpreted as int */ + int istep = __load_via_object(step->value, __pos___data__).intvalue; + + /* Calculate the size of the substring. */ + size_t resultsize = ((iend - istart - 1) / istep) + 1; + int to, from; /* Reserve space for a new string. */ - sub = (char *) __ALLOCATE(l + 1, sizeof(char)); - memcpy(sub, s + i, l); /* does not null terminate but final byte should be zero */ - return __new_str(sub, l); + sub = (char *) __ALLOCATE(resultsize + 1, sizeof(char)); + + /* Does not null terminate but final byte should be zero. */ + if (istep > 0) + for (from = istart, to = 0; from < iend; from += istep, to++) + sub[to] = s[from]; + else if (istep < 0) + for (from = istart, to = 0; from > iend; from += istep, to++) + sub[to] = s[from]; + + return __new_str(sub, resultsize); } /* Module initialisation. */ diff -r 42fed2e5be00 -r f42e303e5dc5 tests/string.py --- a/tests/string.py Mon Dec 12 01:46:28 2016 +0100 +++ b/tests/string.py Mon Dec 12 17:05:59 2016 +0100 @@ -2,6 +2,10 @@ s += " world!" print s # Hello world! print len(s) # 12 +print s[:5] # Hello +print s[5:] # world! +print s[1:10:2] # el ol +print s[10:1:-2] # drwol s2 = "Hello worlds!" print s2 # Hello worlds!