1 #!/usr/bin/env python 2 3 """ 4 String objects. 5 6 Copyright (C) 2015, 2016, 2017 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__.operator import _negate 23 from __builtins__.sequence import hashable, itemaccess 24 from __builtins__.types import check_int 25 from native import str_add, str_lt, str_gt, str_eq, str_len, str_nonempty, \ 26 str_substr 27 28 class basestring(hashable): 29 30 "The base class for all strings." 31 32 def __init__(self, other=None): 33 34 "Initialise the string, perhaps from 'other'." 35 36 # Note the __data__ member. Since strings are either initialised from 37 # literals or converted using routines defined for other types, no form 38 # of actual initialisation is performed here. 39 40 # NOTE: Cannot perform "other and other.__data__ or None" since the 41 # NOTE: __data__ attribute is not a normal attribute. 42 43 if other: 44 self.__data__ = other.__data__ 45 else: 46 self.__data__ = None 47 48 # Note the __key__ member. This is also initialised statically. Where 49 # a string is the same as an attribute name, the __key__ member contains 50 # attribute position and code details. 51 52 if other: 53 self.__key__ = other.__key__ 54 else: 55 self.__key__ = None 56 57 def __hash__(self): 58 59 "Return a value for hashing purposes." 60 61 return self._hashvalue(ord) 62 63 def _binary_op(self, op, other): 64 65 "Perform 'op' on this object and 'other' if appropriate." 66 67 # Refuse to operate on specialisations of this class. 68 69 if self.__class__ is not other.__class__: 70 return NotImplemented 71 72 # Otherwise, perform the operation on the operands' data. 73 74 else: 75 return op(self.__data__, other.__data__) 76 77 def _binary_op_rev(self, op, other): 78 79 "Perform 'op' on 'other' and this object if appropriate." 80 81 # Refuse to operate on specialisations of this class. 82 83 if self.__class__ is not other.__class__: 84 return NotImplemented 85 86 # Otherwise, perform the operation on the operands' data. 87 88 else: 89 return op(other.__data__, self.__data__) 90 91 def __iadd__(self, other): 92 93 "Return a string combining this string with 'other'." 94 95 return self._binary_op(str_add, other) 96 97 __add__ = __iadd__ 98 99 def __radd__(self, other): 100 101 "Return a string combining this string with 'other'." 102 103 return self._binary_op_rev(str_add, other) 104 105 def __mul__(self, other): 106 107 "Multiply the string by 'other'." 108 109 b = buffer() 110 111 while other > 0: 112 b.append(self) 113 other -= 1 114 115 return str(b) 116 117 __rmul__ = __mul__ 118 119 def __mod__(self, other): pass 120 def __rmod__(self, other): pass 121 122 def __lt__(self, other): 123 124 "Return whether this string is less than 'other'." 125 126 return self._binary_op(str_lt, other) 127 128 def __gt__(self, other): 129 130 "Return whether this string is greater than 'other'." 131 132 return self._binary_op(str_gt, other) 133 134 def __le__(self, other): 135 136 "Return whether this string is less than or equal to 'other'." 137 138 return _negate(self.__gt__(other)) 139 140 def __ge__(self, other): 141 142 "Return whether this string is greater than or equal to 'other'." 143 144 return _negate(self.__lt__(other)) 145 146 def __eq__(self, other): 147 148 "Return whether this string is equal to 'other'." 149 150 return self._binary_op(str_eq, other) 151 152 def __ne__(self, other): 153 154 "Return whether this string is not equal to 'other'." 155 156 return _negate(self.__eq__(other)) 157 158 def bytelength(self): 159 160 "Return the number of bytes in this string." 161 162 return str_len(self.__data__) 163 164 __len__ = bytelength 165 166 def __str__(self): 167 168 "Return a string representation." 169 170 return self 171 172 def __repr__(self): 173 174 "Return a program representation." 175 176 # NOTE: To be implemented with proper quoting. 177 b = buffer(['"', self, '"']) 178 return str(b) 179 180 def __bool__(self): 181 182 "Return whether the string provides any data." 183 184 return str_nonempty(self.__data__) 185 186 def __contains__(self, value): 187 188 "Return whether this string contains 'value'." 189 190 return self.find(value) != -1 191 192 def endswith(self, s): 193 194 "Return whether this string ends with 's'." 195 196 return self[-s.__len__():] == s 197 198 def find(self, sub, start=None, end=None): 199 200 """ 201 Find 'sub' in the string, starting at 'start' (or 0, if omitted), ending 202 at 'end' (or the end of the string, if omitted), returning -1 if 'sub' 203 is not present. 204 """ 205 206 sublen = sub.__len__() 207 208 i = start or 0 209 end = end or self.__len__() 210 211 while i < end - sublen: 212 if sub == self[i:i+sublen]: 213 return i 214 i += 1 215 216 return -1 217 218 def index(self, sub, start=None, end=None): 219 220 """ 221 Find 'sub' in the string, starting at 'start' (or 0, if omitted), ending 222 at 'end' (or the end of the string, if omitted), raising ValueError if 223 'sub' is not present. 224 """ 225 226 i = self.find(sub, start, end) 227 228 if i == -1: 229 raise ValueError(sub) 230 else: 231 return i 232 233 def join(self, l): 234 235 "Join the elements in 'l' with this string." 236 237 # Empty strings just cause the list elements to be concatenated. 238 239 if not self.__bool__(): 240 return str(buffer(l)) 241 242 # Non-empty strings join the elements together in a buffer. 243 244 b = buffer() 245 first = True 246 247 for s in l: 248 if first: 249 first = False 250 else: 251 b.append(self) 252 b.append(s) 253 254 return str(b) 255 256 def lower(self): pass 257 def lstrip(self, chars=None): pass 258 def replace(self, old, new, count=None): pass 259 def rfind(self, sub, start=None, end=None): pass 260 def rsplit(self, sep=None, maxsplit=None): pass 261 def rstrip(self, chars=None): pass 262 def split(self, sep=None, maxsplit=None): pass 263 def splitlines(self, keepends=False): pass 264 265 def startswith(self, s): 266 267 "Return whether this string starts with 's'." 268 269 return self[:s.__len__()] == s 270 271 def strip(self, chars=None): pass 272 def upper(self): pass 273 274 class string(basestring): 275 276 "A plain string of bytes." 277 278 # Special implementation methods. 279 280 def __get_single_item__(self, index): 281 282 "Return the item at the normalised (positive) 'index'." 283 284 self._check_index(index) 285 return str_substr(self.__data__, index, index + 1, 1) 286 287 def __get_multiple_items__(self, start, end, step): 288 289 """ 290 Return items from 'start' until (but excluding) 'end', at 'step' 291 intervals. 292 """ 293 294 self._check_index(start) 295 self._check_end_index(end) 296 check_int(step) 297 298 if step == 0: 299 raise ValueError(step) 300 301 if start == end: 302 return "" 303 304 return str_substr(self.__data__, start, end, step) 305 306 def str(obj): 307 308 "Return the string representation of 'obj'." 309 310 # Class attributes of instances provide __str__. 311 312 return obj.__str__() 313 314 # vim: tabstop=4 expandtab shiftwidth=4