1 #!/usr/bin/env python 2 3 """ 4 Span-related objects. 5 6 Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from __builtins__.sequence import _max, _min 23 24 class slice: 25 26 "Implementation of slice." 27 28 NO_END = object() 29 30 def __init__(self, start_or_end=None, end=NO_END, step=1): 31 32 "Initialise the slice with the given 'start_or_end', 'end' and 'step'." 33 34 if end is xrange.NO_END: 35 self.start = 0 36 self.end = start_or_end 37 else: 38 self.start = start_or_end 39 self.end = end 40 41 if step == 0: 42 raise ValueError(self.step) 43 44 self.step = step 45 46 def __str__(self): 47 48 "Return a string representation." 49 50 b = buffer([self.__name__, "(", self.start, ", ", self.end, ", ", self.step, ")"]) 51 return str(b) 52 53 __repr__ = __str__ 54 55 class xrange(slice): 56 57 "Implementation of xrange." 58 59 def __init__(self, start_or_end, end=slice.NO_END, step=1): 60 61 "Initialise the xrange with the given 'start_or_end', 'end' and 'step'." 62 63 get_using(slice.__init__, self)(start_or_end, end, step) 64 65 # Constrain the end according to the start and step. 66 67 if step > 0: 68 self.end = _max(self.start, self.end) 69 elif step < 0: 70 self.end = _min(self.start, self.end) 71 else: 72 raise ValueError(self.step) 73 74 def __len__(self): 75 76 "Return the length of the range." 77 78 return (self.end - self.start) / self.step 79 80 def __iter__(self): 81 82 "Return an iterator, currently self." 83 84 return xrangeiterator(self) 85 86 class xrangeiterator: 87 88 "An iterator over an xrange." 89 90 def __init__(self, obj): 91 92 "Initialise the iterator with the given 'obj'." 93 94 self.start = obj.start 95 self.end = obj.end 96 self.step = obj.step 97 self.current = obj.start 98 99 def next(self): 100 101 "Return the next item or raise a StopIteration exception." 102 103 if self.step < 0 and self.current <= self.end or self.step > 0 and self.current >= self.end: 104 raise StopIteration 105 106 current = self.current 107 self.current += self.step 108 return current 109 110 def range(start_or_end, end=None, step=1): 111 112 "Implementation of range." 113 114 return list(xrange(start_or_end, end, step)) 115 116 # vim: tabstop=4 expandtab shiftwidth=4