My favorites | Sign in
Project Home Downloads Wiki Issues Source
Repository:
Checkout   Browse   Changes   Clones  
Changes to /src/lepl/core/trace.py
d0c0c1b67cfa vs. 9a59cecbd954 Compare: vs.  Format:
Revision 9a59cecbd954
Go to: 
Project members, sign in to write a code review
/src/lepl/core/trace.py   d0c0c1b67cfa /src/lepl/core/trace.py   9a59cecbd954
1 1
2 # The contents of this file are subject to the Mozilla Public License 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 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 4 # in compliance with the License. You may obtain a copy of the License
5 # at http://www.mozilla.org/MPL/ 5 # at http://www.mozilla.org/MPL/
6 # 6 #
7 # Software distributed under the License is distributed on an "AS IS" 7 # Software distributed under the License is distributed on an "AS IS"
8 # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9 # the License for the specific language governing rights and 9 # the License for the specific language governing rights and
10 # limitations under the License. 10 # limitations under the License.
11 # 11 #
12 # The Original Code is LEPL (http://www.acooke.org/lepl) 12 # The Original Code is LEPL (http://www.acooke.org/lepl)
13 # The Initial Developer of the Original Code is Andrew Cooke. 13 # The Initial Developer of the Original Code is Andrew Cooke.
14 # Portions created by the Initial Developer are Copyright (C) 2009-2010 14 # Portions created by the Initial Developer are Copyright (C) 2009-2010
15 # Andrew Cooke (andrew@acooke.org). All Rights Reserved. 15 # Andrew Cooke (andrew@acooke.org). All Rights Reserved.
16 # 16 #
17 # Alternatively, the contents of this file may be used under the terms 17 # Alternatively, the contents of this file may be used under the terms
18 # of the LGPL license (the GNU Lesser General Public License, 18 # of the LGPL license (the GNU Lesser General Public License,
19 # http://www.gnu.org/licenses/lgpl.html), in which case the provisions 19 # http://www.gnu.org/licenses/lgpl.html), in which case the provisions
20 # of the LGPL License are applicable instead of those above. 20 # of the LGPL License are applicable instead of those above.
21 # 21 #
22 # If you wish to allow use of your version of this file only under the 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 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 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 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 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 27 # above, a recipient may use your version of this file under either the
28 # MPL or the LGPL License. 28 # MPL or the LGPL License.
29 29
30 ''' 30 '''
31 Tools for logging and tracing. 31 Tools for logging and tracing.
32 ''' 32 '''
33 33
34 # we abuse conventions to give a consistent interface 34 # we abuse conventions to give a consistent interface
35 # pylint: disable-msg=C0103 35 # pylint: disable-msg=C0103
36 36
37 from lepl.stream.core import s_delta, s_line, s_len 37 from lepl.stream.core import s_delta, s_line, s_len
38 from lepl.core.monitor import ActiveMonitor, ValueMonitor, StackMonitor 38 from lepl.core.monitor import ActiveMonitor, ValueMonitor, StackMonitor
39 from lepl.support.lib import CircularFifo, LogMixin, sample, fmt, str 39 from lepl.support.lib import CircularFifo, LogMixin, sample, fmt, str
40 40
41 41
42 def TraceStack(enabled=False): 42 def TraceStack(enabled=False):
43 ''' 43 '''
44 A basic logger (implemented as a monitor - `MonitorInterface`) 44 A basic logger (implemented as a monitor - `MonitorInterface`)
45 that records the flow of control during parsing. It can be controlled by 45 that records the flow of control during parsing. It can be controlled by
46 `Trace()`. 46 `Trace()`.
47 47
48 This is a factory that "escapes" the main class via a function to simplify 48 This is a factory that "escapes" the main class via a function to simplify
49 configuration. 49 configuration.
50 ''' 50 '''
51 return lambda: _TraceStack(enabled) 51 return lambda: _TraceStack(enabled)
52 52
53 53
54 class _TraceStack(ActiveMonitor, ValueMonitor, LogMixin): 54 class _TraceStack(ActiveMonitor, ValueMonitor, LogMixin):
55 ''' 55 '''
56 A basic logger (implemented as a monitor - `MonitorInterface`) 56 A basic logger (implemented as a monitor - `MonitorInterface`)
57 that records the flow of control during parsing. It can be controlled by 57 that records the flow of control during parsing. It can be controlled by
58 `Trace()`. 58 `Trace()`.
59 ''' 59 '''
60 60
61 def __init__(self, enabled=False): 61 def __init__(self, enabled=False):
62 super(_TraceStack, self).__init__() 62 super(_TraceStack, self).__init__()
63 self.generator = None 63 self.generator = None
64 self.depth = -1 64 self.depth = -1
65 self.action = None 65 self.action = None
66 self.enabled = 1 if enabled else 0 66 self.enabled = 1 if enabled else 0
67 self.epoch = 0 67 self.epoch = 0
68 68
69 def next_iteration(self, epoch, value, exception, stack): 69 def next_iteration(self, epoch, value, exception, stack):
70 ''' 70 '''
71 Store epoch and stack size. 71 Store epoch and stack size.
72 ''' 72 '''
73 self.epoch = epoch 73 self.epoch = epoch
74 self.depth = len(stack) 74 self.depth = len(stack)
75 75
76 def before_next(self, generator): 76 def before_next(self, generator):
77 ''' 77 '''
78 Log when enabled. 78 Log when enabled.
79 ''' 79 '''
80 if self.enabled > 0: 80 if self.enabled > 0:
81 self.generator = generator 81 self.generator = generator
82 self.action = fmt('next({0})', generator) 82 self.action = fmt('next({0})', generator)
83 83
84 def after_next(self, value): 84 def after_next(self, value):
85 ''' 85 '''
86 Log when enabled. 86 Log when enabled.
87 ''' 87 '''
88 if self.enabled > 0: 88 if self.enabled > 0:
89 self._log_result(value, self.fmt_result(value)) 89 self._log_result(value, self.fmt_result(value))
90 90
91 def before_throw(self, generator, value): 91 def before_throw(self, generator, value):
92 ''' 92 '''
93 Log when enabled. 93 Log when enabled.
94 ''' 94 '''
95 if self.enabled > 0: 95 if self.enabled > 0:
96 self.generator = generator 96 self.generator = generator
97 if type(value) is StopIteration: 97 if type(value) is StopIteration:
98 self.action = fmt('stop -> {0}', generator) 98 self.action = fmt('stop -> {0}', generator)
99 else: 99 else:
100 self.action = fmt('{1!r} -> {0}', generator, value) 100 self.action = fmt('{1!r} -> {0}', generator, value)
101 101
102 def after_throw(self, value): 102 def after_throw(self, value):
103 ''' 103 '''
104 Log when enabled. 104 Log when enabled.
105 ''' 105 '''
106 if self.enabled > 0: 106 if self.enabled > 0:
107 self._log_result(value, self.fmt_result(value)) 107 self._log_result(value, self.fmt_result(value))
108 108
109 def before_send(self, generator, value): 109 def before_send(self, generator, value):
110 ''' 110 '''
111 Log when enabled. 111 Log when enabled.
112 ''' 112 '''
113 if self.enabled > 0: 113 if self.enabled > 0:
114 self.generator = generator 114 self.generator = generator
115 self.action = fmt('{1!r} -> {0}', generator, value) 115 self.action = fmt('{1!r} -> {0}', generator, value)
116 116
117 def after_send(self, value): 117 def after_send(self, value):
118 ''' 118 '''
119 Log when enabled. 119 Log when enabled.
120 ''' 120 '''
121 if self.enabled > 0: 121 if self.enabled > 0:
122 self._log_result(value, self.fmt_result(value)) 122 self._log_result(value, self.fmt_result(value))
123 123
124 def exception(self, value): 124 def exception(self, value):
125 ''' 125 '''
126 Log when enabled. 126 Log when enabled.
127 ''' 127 '''
128 if self.enabled > 0: 128 if self.enabled > 0:
129 if type(value) is StopIteration: 129 if type(value) is StopIteration:
130 self._log_done(self.fmt_done()) 130 self._log_done(self.fmt_done())
131 else: 131 else:
132 self._log_error(self.fmt_result(value)) 132 self._log_error(self.fmt_result(value))
133 133
134 def fmt_result(self, value): 134 def fmt_result(self, value):
135 ''' 135 '''
136 Provide a standard fmt for the results. 136 Provide a standard fmt for the results.
137 ''' 137 '''
138 (stream, depth, locn) = self.fmt_stream() 138 (stream, depth, locn) = self.fmt_stream()
139 return fmt('{0:05d} {1!r:11s} {2} ({3:04d}) {4:03d} ' 139 return fmt('{0:05d} {1!r:11s} {2} ({3:04d}) {4:03d} '
140 '{5:s} -> {6!r}', 140 '{5:s} -> {6!r}',
141 self.epoch, 141 self.epoch,
142 stream, 142 stream,
143 locn, 143 locn,
144 depth, 144 depth,
145 self.depth, 145 self.depth,
146 self.action, 146 self.action,
147 value) 147 value)
148 148
149 def fmt_done(self): 149 def fmt_done(self):
150 ''' 150 '''
151 Provide a standard fmt for failure. 151 Provide a standard fmt for failure.
152 ''' 152 '''
153 (stream, depth, locn) = self.fmt_stream() 153 (stream, depth, locn) = self.fmt_stream()
154 return fmt('{0:05d} {1!r:11s} {2} ({3:04d}) {4:03d} ' 154 return fmt('{0:05d} {1!r:11s} {2} ({3:04d}) {4:03d} '
155 '{5:s} -> stop', 155 '{5:s} -> stop',
156 self.epoch, 156 self.epoch,
157 stream, 157 stream,
158 locn, 158 locn,
159 depth, 159 depth,
160 self.depth, 160 self.depth,
161 self.action) 161 self.action)
162 162
163 def fmt_stream(self): 163 def fmt_stream(self):
164 ''' 164 '''
165 Provide a standard fmt for location. 165 Provide a standard fmt for location.
166 ''' 166 '''
167 try: 167 try:
168 (offset, lineno, char) = s_delta(self.generator.stream) 168 (offset, lineno, char) = s_delta(self.generator.stream)
169 locn = fmt('{0}/{1}.{2}', offset, lineno, char) 169 locn = fmt('{0}/{1}.{2}', offset, lineno, char)
170 try: 170 try:
171 stream = sample('', s_line(self.generator.stream, False)[0], 9) 171 stream = sample('', s_line(self.generator.stream, False)[0], 9)
172 except StopIteration: 172 except StopIteration:
173 stream = '<EOS>' 173 stream = '<EOS>'
174 return (stream, offset, locn) 174 return (stream, offset, locn)
175 except StopIteration: 175 except StopIteration:
176 return ('<EOS>', -1, '') 176 return ('<EOS>', -1, '')
177 except TypeError: 177 except TypeError:
178 return (self.generator.stream, -1, '') 178 return (self.generator.stream, -1, '')
179 179
180 def yield_(self, value): 180 def yield_(self, value):
181 ''' 181 '''
182 Log when enabled. 182 Log when enabled.
183 ''' 183 '''
184 if self.enabled > 0: 184 if self.enabled > 0:
185 self._info(self.fmt_final_result(value)) 185 self._info(self.fmt_final_result(value))
186 186
187 def raise_(self, value): 187 def raise_(self, value):
188 ''' 188 '''
189 Log when enabled. 189 Log when enabled.
190 ''' 190 '''
191 if self.enabled > 0: 191 if self.enabled > 0:
192 if type(value) is StopIteration: 192 if type(value) is StopIteration:
193 self._info(self.fmt_final_result(fmt('raise {0!r}', value))) 193 self._info(self.fmt_final_result(fmt('raise {0!r}', value)))
194 else: 194 else:
195 self._warn(self.fmt_final_result(fmt('raise {0!r}', value))) 195 self._warn(self.fmt_final_result(fmt('raise {0!r}', value)))
196 196
197 def fmt_final_result(self, value): 197 def fmt_final_result(self, value):
198 ''' 198 '''
199 Provide a standard fmt for the result. 199 Provide a standard fmt for the result.
200 ''' 200 '''
201 return fmt('{0:05d} {1:03d} {2} {3}', 201 return fmt('{0:05d} {1:03d} {2} {3}',
202 self.epoch, 202 self.epoch,
203 self.depth, 203 self.depth,
204 ' ' * 63, 204 ' ' * 63,
205 value) 205 value)
206 206
207 def _log_result(self, value, text): 207 def _log_result(self, value, text):
208 ''' 208 '''
209 Record a result. 209 Record a result.
210 ''' 210 '''
211 (self._info if type(value) is tuple else self._debug)(text) 211 (self._info if type(value) is tuple else self._debug)(text)
212 212
213 def _log_error(self, text): 213 def _log_error(self, text):
214 ''' 214 '''
215 Record an error. 215 Record an error.
216 ''' 216 '''
217 self._warn(text) 217 self._warn(text)
218 218
219 def _log_done(self, text): 219 def _log_done(self, text):
220 ''' 220 '''
221 Record a "stop". 221 Record a "stop".
222 ''' 222 '''
223 self._debug(text) 223 self._debug(text)
224 224
225 def switch(self, increment): 225 def switch(self, increment):
226 ''' 226 '''
227 Called by the `Trace` matcher to turn this on and off. 227 Called by the `Trace` matcher to turn this on and off.
228 ''' 228 '''
229 self.enabled += increment 229 self.enabled += increment
230 230
231 231
232 def RecordDeepest(n_before=6, n_results_after=2, n_done_after=2): 232 def RecordDeepest(n_before=6, n_results_after=2, n_done_after=2):
233 ''' 233 '''
234 A logger (implemented as a monitor - `MonitorInterface`) 234 A logger (implemented as a monitor - `MonitorInterface`)
235 that records the deepest match found during a parse. 235 that records the deepest match found during a parse.
236 236
237 This is a helper function that "escapes" the main class via a function 237 This is a helper function that "escapes" the main class via a function
238 to simplify configuration. 238 to simplify configuration.
239 ''' 239 '''
240 return lambda: _RecordDeepest(n_before, n_results_after, n_done_after) 240 return lambda: _RecordDeepest(n_before, n_results_after, n_done_after)
241 241
242 242
243 class _RecordDeepest(_TraceStack): 243 class _RecordDeepest(_TraceStack):
244 ''' 244 '''
245 A logger (implemented as a monitor - `MonitorInterface`) 245 A logger (implemented as a monitor - `MonitorInterface`)
246 that records the deepest match found during a parse. 246 that records the deepest match found during a parse.
247 ''' 247 '''
248 248
249 def __init__(self, n_before=6, n_results_after=2, n_done_after=2): 249 def __init__(self, n_before=6, n_results_after=2, n_done_after=2):
250 super(_RecordDeepest, self).__init__(enabled=True) 250 super(_RecordDeepest, self).__init__(enabled=True)
251 self.n_before = n_before 251 self.n_before = n_before
252 self.n_results_after = n_results_after 252 self.n_results_after = n_results_after
253 self.n_done_after = n_done_after 253 self.n_done_after = n_done_after
254 self._limited = CircularFifo(n_before) 254 self._limited = CircularFifo(n_before)
255 self._before = [] 255 self._before = []
256 self._results_after = [] 256 self._results_after = []
257 self._done_after = [] 257 self._done_after = []
258 self._deepest = -1e99 258 self._deepest = -1e99
259 self._countdown_result = 0 259 self._countdown_result = 0
260 self._countdown_done = 0 260 self._countdown_done = 0
261 261
262 def _log_result(self, value, text): 262 def _log_result(self, value, text):
263 ''' 263 '''
264 Modify `TraceStack` to record the data. 264 Modify `TraceStack` to record the data.
265 ''' 265 '''
266 if type(value) is tuple: 266 if type(value) is tuple:
267 self.record(True, text) 267 self.record(True, text)
268 268
269 def _log_error(self, text): 269 def _log_error(self, text):
270 ''' 270 '''
271 Modify `TraceStack` to record the data. 271 Modify `TraceStack` to record the data.
272 ''' 272 '''
273 self.record(True, text) 273 self.record(True, text)
274 274
275 def _log_done(self, text): 275 def _log_done(self, text):
276 ''' 276 '''
277 Modify `TraceStack` to record the data. 277 Modify `TraceStack` to record the data.
278 ''' 278 '''
279 self.record(False, text) 279 self.record(False, text)
280 280
281 def record(self, is_result, text): 281 def record(self, is_result, text):
282 ''' 282 '''
283 Record the data. 283 Record the data.
284 ''' 284 '''
285 stream = self.generator.stream
286 try: 285 try:
287 depth = s_delta(stream)[0] 286 stream = self.generator.stream
288 except AttributeError: # no .depth() 287 try:
289 depth = -1 288 depth = s_delta(stream)[0]
290 if depth >= self._deepest and is_result: 289 except AttributeError: # no .depth()
291 self._deepest = depth 290 depth = -1
292 self._countdown_result = self.n_results_after 291 if depth >= self._deepest and is_result:
293 self._countdown_done = self.n_done_after 292 self._deepest = depth
294 self._before = list(self._limited) 293 self._countdown_result = self.n_results_after
295 self._results_after = [] 294 self._countdown_done = self.n_done_after
296 self._done_after = [] 295 self._before = list(self._limited)
297 elif is_result and self._countdown_result: 296 self._results_after = []
298 self._countdown_result -= 1 297 self._done_after = []
299 self._results_after.append(text) 298 elif is_result and self._countdown_result:
300 elif not is_result and self._countdown_done: 299 self._countdown_result -= 1
301 self._countdown_done -= 1 300 self._results_after.append(text)
302 self._done_after.append(text) 301 elif not is_result and self._countdown_done:
303 self._limited.append(text) 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
304 307
305 def yield_(self, value): 308 def yield_(self, value):
306 ''' 309 '''
307 Display the result. 310 Display the result and reset.
308
309 (As I document this code later, it is no longer clear why this does so)
310 ''' 311 '''
311 self._deepest = 0 312 self._deepest = 0
312 self._limited.clear() 313 self._limited.clear()
313 self.__display() 314 self.__display()
314 315
315 def raise_(self, value): 316 def raise_(self, value):
316 ''' 317 '''
317 Display the result. 318 Display the result and reset.
318
319 (As I document this code later, it is no longer clear why this does so)
320 ''' 319 '''
321 self._deepest = 0 320 self._deepest = 0
322 self._limited.clear() 321 self._limited.clear()
323 self.__display() 322 self.__display()
324 323
325 def __display(self): 324 def __display(self):
326 ''' 325 '''
327 Display the result. 326 Display the result.
328 ''' 327 '''
329 self._info(self.__fmt()) 328 self._info(self.__fmt())
330 329
331 def __fmt(self): 330 def __fmt(self):
332 ''' 331 '''
333 fmt the result. 332 fmt the result.
334 ''' 333 '''
335 return fmt( 334 return fmt(
336 '\nUp to {0} matches before and including longest match:\n{1}\n' 335 '\nUp to {0} matches before and including longest match:\n{1}\n'
337 'Up to {2} failures following longest match:\n{3}\n' 336 'Up to {2} failures following longest match:\n{3}\n'
338 'Up to {4} successful matches following longest match:\n{5}\n', 337 'Up to {4} successful matches following longest match:\n{5}\n',
339 self.n_before, '\n'.join(self._before), 338 self.n_before, '\n'.join(self._before),
340 self.n_done_after, '\n'.join(self._done_after), 339 self.n_done_after, '\n'.join(self._done_after),
341 self.n_results_after, '\n'.join(self._results_after)) 340 self.n_results_after, '\n'.join(self._results_after))
342 341
343 342
344 343
Powered by Google Project Hosting