Package lepl :: Package core :: Module trace
[hide private]
[frames] | no frames]

Source Code for Module lepl.core.trace

  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  Tools for logging and tracing. 
 32  ''' 
 33   
 34  # we abuse conventions to give a consistent interface  
 35  # pylint: disable-msg=C0103 
 36   
 37  from lepl.stream.core import s_delta, s_line, s_len 
 38  from lepl.core.monitor import ActiveMonitor, ValueMonitor, StackMonitor 
 39  from lepl.support.lib import CircularFifo, LogMixin, sample, fmt, str 
 40   
 41   
42 -def TraceStack(enabled=False):
43 ''' 44 A basic logger (implemented as a monitor - `MonitorInterface`) 45 that records the flow of control during parsing. It can be controlled by 46 `Trace()`. 47 48 This is a factory that "escapes" the main class via a function to simplify 49 configuration. 50 ''' 51 return lambda: _TraceStack(enabled)
52 53
54 -class _TraceStack(ActiveMonitor, ValueMonitor, LogMixin):
55 ''' 56 A basic logger (implemented as a monitor - `MonitorInterface`) 57 that records the flow of control during parsing. It can be controlled by 58 `Trace()`. 59 ''' 60
61 - def __init__(self, enabled=False):
62 super(_TraceStack, self).__init__() 63 self.generator = None 64 self.depth = -1 65 self.action = None 66 self.enabled = 1 if enabled else 0 67 self.epoch = 0
68
69 - def next_iteration(self, epoch, value, exception, stack):
70 ''' 71 Store epoch and stack size. 72 ''' 73 self.epoch = epoch 74 self.depth = len(stack)
75
76 - def before_next(self, generator):
77 ''' 78 Log when enabled. 79 ''' 80 if self.enabled > 0: 81 self.generator = generator 82 self.action = fmt('next({0})', generator)
83
84 - def after_next(self, value):
85 ''' 86 Log when enabled. 87 ''' 88 if self.enabled > 0: 89 self._log_result(value, self.fmt_result(value))
90
91 - def before_throw(self, generator, value):
92 ''' 93 Log when enabled. 94 ''' 95 if self.enabled > 0: 96 self.generator = generator 97 if type(value) is StopIteration: 98 self.action = fmt('stop -> {0}', generator) 99 else: 100 self.action = fmt('{1!r} -> {0}', generator, value)
101
102 - def after_throw(self, value):
103 ''' 104 Log when enabled. 105 ''' 106 if self.enabled > 0: 107 self._log_result(value, self.fmt_result(value))
108
109 - def before_send(self, generator, value):
110 ''' 111 Log when enabled. 112 ''' 113 if self.enabled > 0: 114 self.generator = generator 115 self.action = fmt('{1!r} -> {0}', generator, value)
116
117 - def after_send(self, value):
118 ''' 119 Log when enabled. 120 ''' 121 if self.enabled > 0: 122 self._log_result(value, self.fmt_result(value))
123
124 - def exception(self, value):
125 ''' 126 Log when enabled. 127 ''' 128 if self.enabled > 0: 129 if type(value) is StopIteration: 130 self._log_done(self.fmt_done()) 131 else: 132 self._log_error(self.fmt_result(value))
133
134 - def fmt_result(self, value):
135 ''' 136 Provide a standard fmt for the results. 137 ''' 138 (stream, depth, locn) = self.fmt_stream() 139 return fmt('{0:05d} {1!r:11s} {2} ({3:04d}) {4:03d} ' 140 '{5:s} -> {6!r}', 141 self.epoch, 142 stream, 143 locn, 144 depth, 145 self.depth, 146 self.action, 147 value)
148
149 - def fmt_done(self):
150 ''' 151 Provide a standard fmt for failure. 152 ''' 153 (stream, depth, locn) = self.fmt_stream() 154 return fmt('{0:05d} {1!r:11s} {2} ({3:04d}) {4:03d} ' 155 '{5:s} -> stop', 156 self.epoch, 157 stream, 158 locn, 159 depth, 160 self.depth, 161 self.action)
162
163 - def fmt_stream(self):
164 ''' 165 Provide a standard fmt for location. 166 ''' 167 try: 168 (offset, lineno, char) = s_delta(self.generator.stream) 169 locn = fmt('{0}/{1}.{2}', offset, lineno, char) 170 try: 171 stream = sample('', s_line(self.generator.stream, False)[0], 9) 172 except StopIteration: 173 stream = '<EOS>' 174 return (stream, offset, locn) 175 except StopIteration: 176 return ('<EOS>', -1, '') 177 except TypeError: 178 return (self.generator.stream, -1, '')
179
180 - def yield_(self, value):
181 ''' 182 Log when enabled. 183 ''' 184 if self.enabled > 0: 185 self._info(self.fmt_final_result(value))
186
187 - def raise_(self, value):
188 ''' 189 Log when enabled. 190 ''' 191 if self.enabled > 0: 192 if type(value) is StopIteration: 193 self._info(self.fmt_final_result(fmt('raise {0!r}', value))) 194 else: 195 self._warn(self.fmt_final_result(fmt('raise {0!r}', value)))
196
197 - def fmt_final_result(self, value):
198 ''' 199 Provide a standard fmt for the result. 200 ''' 201 return fmt('{0:05d} {1:03d} {2} {3}', 202 self.epoch, 203 self.depth, 204 ' ' * 63, 205 value)
206
207 - def _log_result(self, value, text):
208 ''' 209 Record a result. 210 ''' 211 (self._info if type(value) is tuple else self._debug)(text)
212
213 - def _log_error(self, text):
214 ''' 215 Record an error. 216 ''' 217 self._warn(text)
218
219 - def _log_done(self, text):
220 ''' 221 Record a "stop". 222 ''' 223 self._debug(text)
224
225 - def switch(self, increment):
226 ''' 227 Called by the `Trace` matcher to turn this on and off. 228 ''' 229 self.enabled += increment
230 231
232 -def RecordDeepest(n_before=6, n_results_after=2, n_done_after=2):
233 ''' 234 A logger (implemented as a monitor - `MonitorInterface`) 235 that records the deepest match found during a parse. 236 237 This is a helper function that "escapes" the main class via a function 238 to simplify configuration. 239 ''' 240 return lambda: _RecordDeepest(n_before, n_results_after, n_done_after)
241 242
243 -class _RecordDeepest(_TraceStack):
244 ''' 245 A logger (implemented as a monitor - `MonitorInterface`) 246 that records the deepest match found during a parse. 247 ''' 248
249 - def __init__(self, n_before=6, n_results_after=2, n_done_after=2):
250 super(_RecordDeepest, self).__init__(enabled=True) 251 self.n_before = n_before 252 self.n_results_after = n_results_after 253 self.n_done_after = n_done_after 254 self._limited = CircularFifo(n_before) 255 self._before = [] 256 self._results_after = [] 257 self._done_after = [] 258 self._deepest = -1e99 259 self._countdown_result = 0 260 self._countdown_done = 0
261
262 - def _log_result(self, value, text):
263 ''' 264 Modify `TraceStack` to record the data. 265 ''' 266 if type(value) is tuple: 267 self.record(True, text)
268
269 - def _log_error(self, text):
270 ''' 271 Modify `TraceStack` to record the data. 272 ''' 273 self.record(True, text)
274
275 - def _log_done(self, text):
276 ''' 277 Modify `TraceStack` to record the data. 278 ''' 279 self.record(False, text)
280
281 - def record(self, is_result, text):
282 ''' 283 Record the data. 284 ''' 285 try: 286 stream = self.generator.stream 287 try: 288 depth = s_delta(stream)[0] 289 except AttributeError: # no .depth() 290 depth = -1 291 if depth >= self._deepest and is_result: 292 self._deepest = depth 293 self._countdown_result = self.n_results_after 294 self._countdown_done = self.n_done_after 295 self._before = list(self._limited) 296 self._results_after = [] 297 self._done_after = [] 298 elif is_result and self._countdown_result: 299 self._countdown_result -= 1 300 self._results_after.append(text) 301 elif not is_result and self._countdown_done: 302 self._countdown_done -= 1 303 self._done_after.append(text) 304 self._limited.append(text) 305 except StopIteration: # end of iterator stream 306 pass
307
308 - def yield_(self, value):
309 ''' 310 Display the result and reset. 311 ''' 312 self._deepest = 0 313 self._limited.clear() 314 self.__display()
315
316 - def raise_(self, value):
317 ''' 318 Display the result and reset. 319 ''' 320 self._deepest = 0 321 self._limited.clear() 322 self.__display()
323
324 - def __display(self):
325 ''' 326 Display the result. 327 ''' 328 self._info(self.__fmt())
329
330 - def __fmt(self):
331 ''' 332 fmt the result. 333 ''' 334 return fmt( 335 '\nUp to {0} matches before and including longest match:\n{1}\n' 336 'Up to {2} failures following longest match:\n{3}\n' 337 'Up to {4} successful matches following longest match:\n{5}\n', 338 self.n_before, '\n'.join(self._before), 339 self.n_done_after, '\n'.join(self._done_after), 340 self.n_results_after, '\n'.join(self._results_after))
341