Package lepl :: Package stream :: Module simple
[hide private]
[frames] | no frames]

Source Code for Module lepl.stream.simple

  1   
  2  # The contents of this file are subject to the Mozilla Public License 
  3  # (MPL) Version 1.1 (the "License"); you may not use this file except 
  4  # in compliance with the License. You may obtain a copy of the License 
  5  # at http://www.mozilla.org/MPL/ 
  6  # 
  7  # Software distributed under the License is distributed on an "AS IS" 
  8  # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
  9  # the License for the specific language governing rights and 
 10  # limitations under the License. 
 11  # 
 12  # The Original Code is LEPL (http://www.acooke.org/lepl) 
 13  # The Initial Developer of the Original Code is Andrew Cooke. 
 14  # Portions created by the Initial Developer are Copyright (C) 2009-2010 
 15  # Andrew Cooke (andrew@acooke.org). All Rights Reserved. 
 16  # 
 17  # Alternatively, the contents of this file may be used under the terms 
 18  # of the LGPL license (the GNU Lesser General Public License, 
 19  # http://www.gnu.org/licenses/lgpl.html), in which case the provisions 
 20  # of the LGPL License are applicable instead of those above. 
 21  # 
 22  # If you wish to allow use of your version of this file only under the 
 23  # terms of the LGPL License and not to allow others to use your version 
 24  # of this file under the MPL, indicate your decision by deleting the 
 25  # provisions above and replace them with the notice and other provisions 
 26  # required by the LGPL License.  If you do not delete the provisions 
 27  # above, a recipient may use your version of this file under either the 
 28  # MPL or the LGPL License. 
 29   
 30  ''' 
 31  Default implementation of the helper classes for sequences (strings and lists). 
 32   
 33  The state is an integer offset.  Sequence and a possible delta for the  
 34  offset are stored in the helper. 
 35  ''' 
 36   
 37  from itertools import chain 
 38   
 39  from lepl.support.lib import fmt, add_defaults, str, LogMixin 
 40  from lepl.stream.core import StreamHelper, OFFSET, LINENO, CHAR, HashKey 
 41   
 42   
43 -class BaseHelper(LogMixin, StreamHelper):
44
45 - def __init__(self, id=None, factory=None, max=None, global_kargs=None, 46 cache_level=None, delta=None):
47 super(BaseHelper, self).__init__(id=id, factory=factory, max=max, 48 global_kargs=global_kargs, cache_level=cache_level) 49 self._delta = delta if delta else (0,1,1)
50 51
52 -class SequenceHelper(BaseHelper):
53
54 - def __init__(self, sequence, id=None, factory=None, max=None, 55 global_kargs=None, cache_level=None, delta=None):
56 super(SequenceHelper, self).__init__(id=id, factory=factory, max=max, 57 global_kargs=global_kargs, cache_level=cache_level, delta=delta) 58 self._sequence = sequence 59 type_ = self._typename(sequence) 60 add_defaults(self.global_kargs, { 61 'global_type': type_, 62 'filename': type_}) 63 self._kargs = dict(self.global_kargs) 64 add_defaults(self._kargs, {'type': type_})
65
66 - def key(self, state, other):
67 # avoid confusion with incremental ids 68 offset = (state + self._delta[OFFSET]) << 16 69 key = HashKey(self.id ^ offset ^ hash(other), (self.id, hash(other))) 70 #self._debug(fmt('Hash at offset {0}: {1}', offset, hash(key))) 71 return key
72
73 - def _fmt(self, sequence, offset, maxlen=60, left='', right='', index=True):
74 '''fmt a possibly long subsection of data.''' 75 if not sequence: 76 if index: 77 return fmt('{0!r}[{1:d}]', sequence, offset) 78 else: 79 return fmt('{0!r}', sequence) 80 if offset >= 0 and offset < len(sequence): 81 centre = offset 82 elif offset > 0: 83 centre = len(sequence) - 1 84 else: 85 centre = 0 86 begin, end = centre, centre+1 87 longest = None 88 while True: 89 if begin > 0: 90 if end < len(sequence): 91 template = '{0!s}...{1!s}...{2!s}' 92 else: 93 template = '{0!s}...{1!s}{2!s}' 94 else: 95 if end < len(sequence): 96 template = '{0!s}{1!s}...{2!s}' 97 else: 98 template = '{0!s}{1!s}{2!s}' 99 body = repr(sequence[begin:end])[len(left):] 100 if len(right): 101 body = body[:-len(right)] 102 text = fmt(template, left, body, right, offset) 103 if index: 104 text = fmt('{0!s}[{1:d}:]', text, offset) 105 if longest is None or len(text) <= maxlen: 106 longest = text 107 if len(text) > maxlen: 108 return longest 109 begin -= 1 110 end += 1 111 if begin < 0 and end > len(sequence): 112 return longest 113 begin = max(begin, 0) 114 end = min(end, len(sequence))
115
116 - def _location(self, kargs, prefix):
117 '''Location (separate method so subclasses can replace).''' 118 return fmt('offset {' + prefix + 'global_offset}, value {' + prefix + 'repr}', 119 **kargs)
120
121 - def _typename(self, instance):
122 if isinstance(instance, list) and instance: 123 return fmt('<list{0}>', self._typename(instance[0])) 124 else: 125 try: 126 return fmt('<{0}>', instance.__class__.__name__) 127 except: 128 return '<unknown>'
129
130 - def kargs(self, state, prefix='', kargs=None):
131 ''' 132 Generate a dictionary of values that describe the stream. These 133 may be extended by subclasses. They are provided to 134 `syntax_error_kargs`, for example. 135 136 Note: Calculating this can be expensive; use only for error messages, 137 not debug messages (that may be discarded). 138 139 Implementation note: Because some values are 140 ''' 141 offset = state + self._delta[OFFSET] 142 if kargs is None: kargs = {} 143 add_defaults(kargs, self._kargs, prefix=prefix) 144 within = offset > -1 and offset < len(self._sequence) 145 data = self._fmt(self._sequence, state) 146 text = self._fmt(self._sequence, state, index=False) 147 # some values below may be already present in self._global_kargs 148 defaults = {'data': data, 149 'global_data': data, 150 'text': text, 151 'global_text': text, 152 'offset': state, 153 'global_offset': offset, 154 'rest': self._fmt(self._sequence[offset:], 0, index=False), 155 'repr': repr(self._sequence[offset]) if within else '<EOS>', 156 'str': str(self._sequence[offset]) if within else '', 157 'lineno': 1, 158 'char': offset+1} 159 add_defaults(kargs, defaults, prefix=prefix) 160 add_defaults(kargs, {prefix + 'location': self._location(kargs, prefix)}) 161 return kargs
162
163 - def next(self, state, count=1):
164 new_state = state+count 165 if new_state <= len(self._sequence): 166 stream = (new_state, self) 167 self.max.update(self._delta[OFFSET] + new_state - 1, stream) 168 return (self._sequence[state:new_state], stream) 169 else: 170 raise StopIteration
171
172 - def join(self, state, *values):
173 assert values, 'Cannot join zero general sequences' 174 result = values[0] 175 for value in values[1:]: 176 result += value 177 return result
178
179 - def empty(self, state):
180 return state >= len(self._sequence)
181
182 - def line(self, state, empty_ok):
183 '''Returns the rest of the data.''' 184 new_state = len(self._sequence) 185 if state < new_state or (empty_ok and state == new_state): 186 stream = (new_state, self) 187 self.max.update(self._delta[OFFSET] + new_state, stream) 188 return (self._sequence[state:new_state], stream) 189 else: 190 raise StopIteration
191
192 - def len(self, state):
193 return len(self._sequence) - state
194
195 - def stream(self, state, value, id_=None, max=None):
196 id_ = self.id if id_ is None else id_ 197 max = max if max else self.max 198 # increment the cache level to expose lower level streams 199 return self.factory(value, id=id_, factory=self.factory, 200 max=max, global_kargs=self.global_kargs, 201 cache_level=self.cache_level+1, 202 delta=self.delta(state))
203
204 - def deepest(self):
205 return self.max.get()
206
207 - def debug(self, state):
208 try: 209 return fmt('{0:d}:{1!r}', state, self._sequence[state]) 210 except IndexError: 211 return fmt('{0:d}:<EOS>', state)
212
213 - def delta(self, state):
214 offset = state + self._delta[OFFSET] 215 return (offset, 1, offset+1)
216
217 - def new_max(self, state):
218 return (self.max, 219 (state, type(self)(self._sequence, id=self.id, 220 factory=self.factory, max=None, 221 global_kargs=self.global_kargs, 222 delta=self._delta)))
223 224 225
226 -class StringHelper(SequenceHelper):
227 ''' 228 String-specific fmtting and location. 229 ''' 230
231 - def __init__(self, sequence, id=None, factory=None, max=None, 232 global_kargs=None, cache_level=None, delta=None):
233 # avoid duplicating processing on known strings 234 if id is None: 235 id = hash(sequence) 236 super(StringHelper, self).__init__(sequence, id=id, factory=factory, 237 max=max, global_kargs=global_kargs, cache_level=cache_level, 238 delta=delta)
239
240 - def _fmt(self, sequence, offset, maxlen=60, left="'", right="'", index=True):
241 return super(StringHelper, self)._fmt(sequence, offset, maxlen=maxlen, 242 left=left, right=right, index=index)
243
244 - def _location(self, kargs, prefix):
245 return fmt('line {' + prefix + 'lineno:d}, character {' + prefix + 'char:d}', **kargs)
246
247 - def delta(self, state):
248 offset = self._delta[OFFSET] + state 249 lineno = self._delta[LINENO] + self._sequence.count('\n', 0, state) 250 start = self._sequence.rfind('\n', 0, state) 251 if start > -1: 252 char = state - start 253 else: 254 char = self._delta[CHAR] + state 255 return (offset, lineno, char)
256
257 - def kargs(self, state, prefix='', kargs=None):
258 if kargs is None: kargs = {} 259 (_, lineno, char) = self.delta(state) 260 start = self._sequence.rfind('\n', 0, state) + 1 # omit \n 261 end = self._sequence.find('\n', state) # omit \n 262 # all is str() because passed to SyntaxError constructor 263 if end < 0: 264 rest = repr(self._sequence[state:]) 265 all = str(self._sequence[start:]) 266 else: 267 rest = repr(self._sequence[state:end]) 268 all = str(self._sequence[start:end]) 269 add_defaults(kargs, { 270 'type': '<string>', 271 'filename': '<string>', 272 'rest': rest, 273 'all': all, 274 'lineno': lineno, 275 'char': char}, prefix=prefix) 276 return super(StringHelper, self).kargs(state, prefix=prefix, kargs=kargs)
277
278 - def join(self, state, *values):
279 return str().join(values)
280
281 - def line(self, state, empty_ok):
282 '''Returns up to, and including then next \n''' 283 max_len = len(self._sequence) 284 if state < max_len or (empty_ok and state == max_len): 285 end = self._sequence.find('\n', state) + 1 286 if not end: end = len(self._sequence) 287 return (self._sequence[state:end], (end, self)) 288 else: 289 raise StopIteration
290
291 - def stream(self, state, value, id_=None, max=None):
292 id_ = self.id if id_ is None else id_ 293 max = max if max else self.max 294 return self.factory(value, id=id_, factory=self.factory, 295 max=max, global_kargs=self.global_kargs, 296 delta=self.delta(state))
297 298
299 -class ListHelper(SequenceHelper):
300 ''' 301 List-specific fprmatting 302 ''' 303
304 - def _fmt(self, sequence, offset, maxlen=60, left="[", right="]", index=True):
305 return super(ListHelper, self)._fmt(sequence, offset, maxlen=maxlen, 306 left=left, right=right, index=index)
307
308 - def join(self, state, *values):
309 return list(chain(*values))
310