Commit | Line | Data |
---|---|---|
6ca2fce3 | 1 | ;;; semantic/debug.el --- Language Debugger framework |
a6de3d1a CY |
2 | |
3 | ;;; Copyright (C) 2003, 2004, 2005, 2008 Free Software Foundation, Inc. | |
4 | ||
5 | ;; Author: Eric M. Ludlam <zappo@gnu.org> | |
6 | ||
7 | ;; This file is part of GNU Emacs. | |
8 | ||
9 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
10 | ;; it under the terms of the GNU General Public License as published by | |
11 | ;; the Free Software Foundation, either version 3 of the License, or | |
12 | ;; (at your option) any later version. | |
13 | ||
14 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | ;; GNU General Public License for more details. | |
18 | ||
19 | ;; You should have received a copy of the GNU General Public License | |
20 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
21 | ||
22 | ;;; Commentary: | |
23 | ;; | |
24 | ;; To provide better support for debugging parsers, this framework | |
25 | ;; provides the interface for debugging. The work of parsing and | |
26 | ;; controlling and stepping through the parsing work must be implemented | |
27 | ;; by the parser. | |
28 | ;; | |
29 | ;; Fortunatly, the nature of language support files means that the parser | |
30 | ;; may not need to be instrumented first. | |
31 | ;; | |
32 | ;; The debugger uses EIEIO objects. One object controls the user | |
33 | ;; interface, including stepping, data-view, queries. A second | |
34 | ;; object implemented here represents the parser itself. A third represents | |
35 | ;; a parser independent frame which knows how to highlight the parser buffer. | |
36 | ;; Each parser must implement the interface and override any methods as needed. | |
37 | ;; | |
38 | ||
39 | (require 'semantic) | |
40 | (require 'eieio) | |
a6de3d1a CY |
41 | |
42 | ;;; Code: | |
43 | (defvar semantic-debug-parser-source nil | |
44 | "For any buffer, the file name (no path) of the parser. | |
45 | This would be a parser for a specific language, not the source | |
46 | to one of the parser generators.") | |
47 | (make-variable-buffer-local 'semantic-debug-parser-source) | |
48 | ||
49 | (defvar semantic-debug-parser-class nil | |
50 | "Class to create when building a debug parser object.") | |
51 | (make-variable-buffer-local 'semantic-debug-parser-class) | |
52 | ||
53 | (defvar semantic-debug-enabled nil | |
54 | "Non-nil when debugging a parser.") | |
55 | ||
56 | ;;; Variables used during a debug session. | |
57 | (defvar semantic-debug-current-interface nil | |
58 | "The debugger interface currently active for this buffer.") | |
59 | ||
60 | (defvar semantic-debug-current-parser nil | |
61 | "The parser current active for this buffer.") | |
62 | ||
63 | ;;; User Interface Portion | |
64 | ;; | |
65 | (defclass semantic-debug-interface () | |
66 | ((parser-buffer :initarg :parser-buffer | |
67 | :type buffer | |
68 | :documentation | |
69 | "The buffer containing the parser we are debugging.") | |
70 | (parser-local-map :initarg :parser-local-map | |
71 | :type keymap | |
72 | :documentation | |
73 | "The local keymap originally in the PARSER buffer.") | |
74 | (parser-location :type marker | |
75 | :documentation | |
76 | "A marker representing where we are in the parser buffer.") | |
77 | (source-buffer :initarg :source-buffer | |
78 | :type buffer | |
79 | :documentation | |
80 | "The buffer containing the source we are parsing. | |
81 | The :parser-buffer defines a parser that can parse the text in the | |
82 | :source-buffer.") | |
83 | (source-local-map :initarg :source-local-map | |
84 | :type keymap | |
85 | :documentation | |
86 | "The local keymap originally in the SOURCE buffer.") | |
87 | (source-location :type marker | |
88 | :documentation | |
89 | "A marker representing where we are in the parser buffer.") | |
90 | (data-buffer :initarg :data-buffer | |
91 | :type buffer | |
92 | :documentation | |
93 | "Buffer being used to display some useful data. | |
94 | These buffers are brought into view when layout occurs.") | |
95 | (current-frame :type semantic-debug-frame | |
96 | :documentation | |
97 | "The currently displayed frame.") | |
98 | (overlays :type list | |
99 | :initarg nil | |
100 | :documentation | |
101 | "Any active overlays being used to show the debug position.") | |
102 | ) | |
103 | "Controls action when in `semantic-debug-mode'") | |
104 | ||
105 | ;; Methods | |
106 | (defmethod semantic-debug-set-frame ((iface semantic-debug-interface) frame) | |
107 | "Set the current frame on IFACE to FRAME." | |
108 | (if frame | |
109 | (oset iface current-frame frame) | |
110 | (slot-makeunbound iface 'current-frame))) | |
111 | ||
112 | (defmethod semantic-debug-set-parser-location ((iface semantic-debug-interface) point) | |
113 | "Set the parser location in IFACE to POINT." | |
114 | (save-excursion | |
115 | (set-buffer (oref iface parser-buffer)) | |
116 | (if (not (slot-boundp iface 'parser-location)) | |
117 | (oset iface parser-location (make-marker))) | |
118 | (move-marker (oref iface parser-location) point)) | |
119 | ) | |
120 | ||
121 | (defmethod semantic-debug-set-source-location ((iface semantic-debug-interface) point) | |
122 | "Set the source location in IFACE to POINT." | |
123 | (save-excursion | |
124 | (set-buffer (oref iface source-buffer)) | |
125 | (if (not (slot-boundp iface 'source-location)) | |
126 | (oset iface source-location (make-marker))) | |
127 | (move-marker (oref iface source-location) point)) | |
128 | ) | |
129 | ||
130 | (defmethod semantic-debug-interface-layout ((iface semantic-debug-interface)) | |
131 | "Layout windows in the current frame to facilitate debugging." | |
132 | (delete-other-windows) | |
133 | ;; Deal with the data buffer | |
134 | (when (slot-boundp iface 'data-buffer) | |
135 | (let ((lines (/ (frame-height (selected-frame)) 3)) | |
136 | (cnt (save-excursion | |
137 | (set-buffer (oref iface data-buffer)) | |
138 | (count-lines (point-min) (point-max)))) | |
139 | ) | |
140 | ;; Set the number of lines to 1/3, or the size of the data buffer. | |
141 | (if (< cnt lines) (setq cnt lines)) | |
6ca2fce3 | 142 | |
a6de3d1a CY |
143 | (split-window-vertically cnt) |
144 | (switch-to-buffer (oref iface data-buffer)) | |
145 | ) | |
146 | (other-window 1)) | |
147 | ;; Parser | |
148 | (switch-to-buffer (oref iface parser-buffer)) | |
149 | (when (slot-boundp iface 'parser-location) | |
150 | (goto-char (oref iface parser-location))) | |
151 | (split-window-vertically) | |
152 | (other-window 1) | |
153 | ;; Source | |
154 | (switch-to-buffer (oref iface source-buffer)) | |
155 | (when (slot-boundp iface 'source-location) | |
156 | (goto-char (oref iface source-location))) | |
157 | ) | |
158 | ||
159 | (defmethod semantic-debug-highlight-lexical-token ((iface semantic-debug-interface) token) | |
160 | "For IFACE, highlight TOKEN in the source buffer . | |
161 | TOKEN is a lexical token." | |
162 | (set-buffer (oref iface :source-buffer)) | |
163 | ||
164 | (object-add-to-list iface 'overlays | |
165 | (semantic-lex-highlight-token token)) | |
166 | ||
167 | (semantic-debug-set-source-location iface (semantic-lex-token-start token)) | |
168 | ) | |
169 | ||
170 | (defmethod semantic-debug-highlight-rule ((iface semantic-debug-interface) nonterm &optional rule match) | |
171 | "For IFACE, highlight NONTERM in the parser buffer. | |
172 | NONTERM is the name of the rule currently being processed that shows up | |
173 | as a nonterminal (or tag) in the source buffer. | |
174 | If RULE and MATCH indicies are specified, highlight those also." | |
175 | (set-buffer (oref iface :parser-buffer)) | |
6ca2fce3 | 176 | |
a6de3d1a CY |
177 | (let* ((rules (semantic-find-tags-by-class 'nonterminal (current-buffer))) |
178 | (nt (semantic-find-first-tag-by-name nonterm rules)) | |
179 | (o nil) | |
180 | ) | |
181 | (when nt | |
182 | ;; I know it is the first symbol appearing in the body of this token. | |
183 | (goto-char (semantic-tag-start nt)) | |
6ca2fce3 | 184 | |
a6de3d1a CY |
185 | (setq o (semantic-make-overlay (point) (progn (forward-sexp 1) (point)))) |
186 | (semantic-overlay-put o 'face 'highlight) | |
187 | ||
188 | (object-add-to-list iface 'overlays o) | |
189 | ||
190 | (semantic-debug-set-parser-location iface (semantic-overlay-start o)) | |
191 | ||
192 | (when (and rule match) | |
193 | ||
194 | ;; Rule, an int, is the rule inside the nonterminal we are following. | |
195 | (re-search-forward ":\\s-*") | |
196 | (while (/= 0 rule) | |
197 | (re-search-forward "^\\s-*|\\s-*") | |
198 | (setq rule (1- rule))) | |
199 | ||
200 | ;; Now find the match inside the rule | |
201 | (while (/= 0 match) | |
202 | (forward-sexp 1) | |
203 | (skip-chars-forward " \t") | |
204 | (setq match (1- match))) | |
205 | ||
206 | ;; Now highlight the thingy we find there. | |
207 | (setq o (semantic-make-overlay (point) (progn (forward-sexp 1) (point)))) | |
208 | (semantic-overlay-put o 'face 'highlight) | |
209 | ||
210 | (object-add-to-list iface 'overlays o) | |
211 | ||
212 | ;; If we have a match for a sub-rule, have the parser position | |
213 | ;; move so we can see it in the output window for very long rules. | |
214 | (semantic-debug-set-parser-location iface (semantic-overlay-start o)) | |
215 | ||
216 | )))) | |
217 | ||
218 | (defmethod semantic-debug-unhighlight ((iface semantic-debug-interface)) | |
219 | "Remove all debugging overlays." | |
220 | (mapc 'semantic-overlay-delete (oref iface overlays)) | |
221 | (oset iface overlays nil)) | |
222 | ||
223 | ;; Call from the parser at a breakpoint | |
224 | (defvar semantic-debug-user-command nil | |
225 | "The command the user is requesting.") | |
226 | ||
227 | (defun semantic-debug-break (frame) | |
228 | "Stop parsing now at FRAME. | |
229 | FRAME is an object that represents the parser's view of the | |
230 | current state of the world. | |
231 | This function enters a recursive edit. It returns | |
232 | on an `exit-recursive-edit', or if someone uses one | |
233 | of the `semantic-debug-mode' commands. | |
234 | It returns the command specified. Parsers need to take action | |
235 | on different types of return values." | |
236 | (save-window-excursion | |
237 | ;; Set up displaying information | |
238 | (semantic-debug-mode t) | |
239 | (unwind-protect | |
240 | (progn | |
241 | (semantic-debug-frame-highlight frame) | |
242 | (semantic-debug-interface-layout semantic-debug-current-interface) | |
243 | (condition-case nil | |
244 | ;; Enter recursive edit... wait for user command. | |
245 | (recursive-edit) | |
246 | (error nil))) | |
247 | (semantic-debug-unhighlight semantic-debug-current-interface) | |
248 | (semantic-debug-mode nil)) | |
249 | ;; Find the requested user state. Do something. | |
250 | (let ((returnstate semantic-debug-user-command)) | |
251 | (setq semantic-debug-user-command nil) | |
252 | returnstate) | |
253 | )) | |
254 | ||
255 | ;;; Frame | |
256 | ;; | |
257 | ;; A frame can represent the state at a break point. | |
258 | (defclass semantic-debug-frame () | |
259 | ( | |
260 | ) | |
261 | "One frame representation.") | |
262 | ||
263 | (defmethod semantic-debug-frame-highlight ((frame semantic-debug-frame)) | |
264 | "Highlight one parser frame." | |
6ca2fce3 | 265 | |
a6de3d1a CY |
266 | ) |
267 | ||
268 | (defmethod semantic-debug-frame-info ((frame semantic-debug-frame)) | |
269 | "Display info about this one parser frame." | |
6ca2fce3 | 270 | |
a6de3d1a CY |
271 | ) |
272 | ||
273 | ;;; Major Mode | |
274 | ;; | |
275 | (defvar semantic-debug-mode-map | |
276 | (let ((km (make-sparse-keymap))) | |
277 | (define-key km "n" 'semantic-debug-next) | |
278 | (define-key km " " 'semantic-debug-next) | |
279 | (define-key km "s" 'semantic-debug-step) | |
280 | (define-key km "u" 'semantic-debug-up) | |
281 | (define-key km "d" 'semantic-debug-down) | |
282 | (define-key km "f" 'semantic-debug-fail-match) | |
283 | (define-key km "h" 'semantic-debug-print-state) | |
284 | (define-key km "s" 'semantic-debug-jump-to-source) | |
285 | (define-key km "p" 'semantic-debug-jump-to-parser) | |
286 | (define-key km "q" 'semantic-debug-quit) | |
287 | (define-key km "a" 'semantic-debug-abort) | |
288 | (define-key km "g" 'semantic-debug-go) | |
289 | (define-key km "b" 'semantic-debug-set-breakpoint) | |
290 | ;; Some boring bindings. | |
291 | (define-key km "e" 'eval-expression) | |
6ca2fce3 | 292 | |
a6de3d1a CY |
293 | km) |
294 | "Keymap used when in semantic-debug-node.") | |
295 | ||
296 | (defun semantic-debug-mode (onoff) | |
297 | "Turn `semantic-debug-mode' on and off. | |
298 | Argument ONOFF is non-nil when we are entering debug mode. | |
299 | \\{semantic-debug-mode-map}" | |
300 | (let ((iface semantic-debug-current-interface)) | |
301 | (if onoff | |
302 | ;; Turn it on | |
303 | (save-excursion | |
304 | (set-buffer (oref iface parser-buffer)) | |
305 | ;; Install our map onto this buffer | |
306 | (use-local-map semantic-debug-mode-map) | |
307 | ;; Make the buffer read only | |
308 | (toggle-read-only 1) | |
6ca2fce3 | 309 | |
a6de3d1a CY |
310 | (set-buffer (oref iface source-buffer)) |
311 | ;; Use our map in the source buffer also | |
312 | (use-local-map semantic-debug-mode-map) | |
313 | ;; Make the buffer read only | |
314 | (toggle-read-only 1) | |
315 | ;; Hooks | |
316 | (run-hooks 'semantic-debug-mode-hooks) | |
317 | ) | |
318 | ;; Restore old mode information | |
319 | (save-excursion | |
320 | (set-buffer | |
321 | (oref semantic-debug-current-interface parser-buffer)) | |
322 | (use-local-map | |
323 | (oref semantic-debug-current-interface parser-local-map)) | |
324 | ) | |
325 | (save-excursion | |
326 | (set-buffer | |
327 | (oref semantic-debug-current-interface source-buffer)) | |
328 | (use-local-map | |
329 | (oref semantic-debug-current-interface source-local-map)) | |
330 | ) | |
331 | (run-hooks 'semantic-debug-exit-hooks) | |
332 | ))) | |
333 | ||
334 | (defun semantic-debug () | |
335 | "Parse the current buffer and run in debug mode." | |
336 | (interactive) | |
337 | (if semantic-debug-current-interface | |
338 | (error "You are already in a debug session")) | |
339 | (if (not semantic-debug-parser-class) | |
340 | (error "This major mode does not support parser debugging")) | |
341 | ;; Clear the cache to force a full reparse. | |
342 | (semantic-clear-toplevel-cache) | |
343 | ;; Do the parse | |
344 | (let ((semantic-debug-enabled t) | |
345 | ;; Create an interface | |
346 | (semantic-debug-current-interface | |
347 | (let ((parserb (semantic-debug-find-parser-source))) | |
348 | (semantic-debug-interface | |
349 | "Debug Interface" | |
350 | :parser-buffer parserb | |
351 | :parser-local-map (save-excursion | |
352 | (set-buffer parserb) | |
353 | (current-local-map)) | |
354 | :source-buffer (current-buffer) | |
355 | :source-local-map (current-local-map) | |
356 | ))) | |
357 | ;; Create a parser debug interface | |
358 | (semantic-debug-current-parser | |
359 | (funcall semantic-debug-parser-class "parser")) | |
360 | ) | |
361 | ;; We could recurse into a parser while debugging. | |
362 | ;; Is that a problem? | |
363 | (semantic-fetch-tags) | |
364 | ;; We should turn the auto-parser back on, but don't do it for | |
365 | ;; now until the debugger is working well. | |
366 | )) | |
367 | ||
368 | (defun semantic-debug-find-parser-source () | |
369 | "Return a buffer containing the parser source file for the current buffer. | |
370 | The parser needs to be on the load path, or this routine returns nil." | |
371 | (if (not semantic-debug-parser-source) | |
372 | (error "No parser is associated with this buffer")) | |
373 | (let ((parser (locate-library semantic-debug-parser-source t))) | |
374 | (if parser | |
375 | (find-file-noselect parser) | |
376 | (error "Cannot find parser source. It should be on the load-path")))) | |
377 | ||
378 | ;;; Debugger commands | |
379 | ;; | |
380 | (defun semantic-debug-next () | |
381 | "Perform one parser operation. | |
382 | In the recursive parser, this steps past one match rule. | |
383 | In other parsers, this may be just like `semantic-debug-step'." | |
384 | (interactive) | |
385 | (let ((parser semantic-debug-current-parser)) | |
386 | (semantic-debug-parser-next parser) | |
387 | (exit-recursive-edit) | |
388 | ) | |
389 | ) | |
390 | ||
391 | (defun semantic-debug-step () | |
392 | "Perform one parser operation." | |
393 | (interactive) | |
394 | (let ((parser semantic-debug-current-parser)) | |
395 | (semantic-debug-parser-step parser) | |
396 | (exit-recursive-edit) | |
397 | ) | |
398 | ) | |
399 | ||
400 | (defun semantic-debug-up () | |
401 | "Move highlighting representation up one level." | |
402 | (interactive) | |
403 | (message "Not implemented yet.") | |
404 | ) | |
405 | ||
406 | (defun semantic-debug-down () | |
407 | "Move highlighting representation down one level." | |
408 | (interactive) | |
409 | (message "Not implemented yet.") | |
410 | ) | |
411 | ||
412 | (defun semantic-debug-fail-match () | |
413 | "Artificially fail the current match." | |
414 | (interactive) | |
415 | (let ((parser semantic-debug-current-parser)) | |
416 | (semantic-debug-parser-fail parser) | |
417 | (exit-recursive-edit) | |
418 | ) | |
419 | ) | |
420 | ||
421 | (defun semantic-debug-print-state () | |
422 | "Show interesting parser state." | |
423 | (interactive) | |
424 | (let ((parser semantic-debug-current-parser)) | |
425 | (semantic-debug-parser-print-state parser) | |
426 | ) | |
427 | ) | |
428 | ||
429 | (defun semantic-debug-jump-to-source () | |
430 | "Move cursor to the source code being parsed at the current lexical token." | |
431 | (interactive) | |
432 | (let* ((interface semantic-debug-current-interface) | |
433 | (buf (oref interface source-buffer))) | |
434 | (if (get-buffer-window buf) | |
435 | (progn | |
436 | (select-frame (window-frame (get-buffer-window buf))) | |
437 | (select-window (get-buffer-window buf))) | |
438 | ;; Technically, this should do a window layout operation | |
439 | (switch-to-buffer buf)) | |
440 | ) | |
441 | ) | |
442 | ||
443 | (defun semantic-debug-jump-to-parser () | |
444 | "Move cursor to the parser being debugged." | |
445 | (interactive) | |
446 | (let* ((interface semantic-debug-current-interface) | |
447 | (buf (oref interface parser-buffer))) | |
448 | (if (get-buffer-window buf) | |
449 | (progn | |
450 | (select-frame (window-frame (get-buffer-window buf))) | |
451 | (select-window (get-buffer-window buf))) | |
452 | ;; Technically, this should do a window layout operation | |
453 | (switch-to-buffer buf)) | |
454 | ) | |
455 | ) | |
456 | ||
457 | (defun semantic-debug-quit () | |
458 | "Exit debug mode, blowing all stack, and leaving the parse incomplete. | |
459 | Do not update any tokens already parsed." | |
460 | (interactive) | |
461 | (let ((parser semantic-debug-current-parser)) | |
462 | (semantic-debug-parser-quit parser) | |
463 | (exit-recursive-edit) | |
464 | ) | |
465 | ) | |
466 | ||
467 | (defun semantic-debug-abort () | |
468 | "Abort one level of debug mode, blowing all stack." | |
469 | (interactive) | |
470 | (let ((parser semantic-debug-current-parser)) | |
471 | (semantic-debug-parser-abort parser) | |
472 | (exit-recursive-edit) | |
473 | ) | |
474 | ) | |
475 | ||
476 | (defun semantic-debug-go () | |
477 | "Continue parsing till finish or breakpoint." | |
478 | (interactive) | |
479 | (let ((parser semantic-debug-current-parser)) | |
480 | (semantic-debug-parser-go parser) | |
481 | (exit-recursive-edit) | |
482 | ) | |
483 | ) | |
484 | ||
485 | (defun semantic-debug-set-breakpoint () | |
486 | "Set a breakpoint at the current rule location." | |
487 | (interactive) | |
488 | (let ((parser semantic-debug-current-parser) | |
489 | ;; Get the location as semantic tokens. | |
490 | (location (semantic-current-tag)) | |
491 | ) | |
492 | (if location | |
493 | (semantic-debug-parser-break parser location) | |
494 | (error "Not on a rule")) | |
495 | ) | |
496 | ) | |
497 | ||
498 | ||
499 | ;;; Debugger superclass | |
500 | ;; | |
501 | (defclass semantic-debug-parser () | |
502 | ( | |
503 | ) | |
504 | "Represents a parser and its state. | |
505 | When implementing the debug parser you can add extra functionality | |
506 | by overriding one of the command methods. Be sure to use | |
507 | `call-next-method' so that the debug command is saved, and passed | |
508 | down to your parser later." | |
509 | :abstract t) | |
510 | ||
511 | (defmethod semantic-debug-parser-next ((parser semantic-debug-parser)) | |
512 | "Execute next for this PARSER." | |
513 | (setq semantic-debug-user-command 'next) | |
514 | ) | |
515 | ||
516 | (defmethod semantic-debug-parser-step ((parser semantic-debug-parser)) | |
517 | "Execute a step for this PARSER." | |
518 | (setq semantic-debug-user-command 'step) | |
519 | ) | |
520 | ||
521 | (defmethod semantic-debug-parser-go ((parser semantic-debug-parser)) | |
522 | "Continue executiong in this PARSER until the next breakpoint." | |
523 | (setq semantic-debug-user-command 'go) | |
524 | ) | |
525 | ||
526 | (defmethod semantic-debug-parser-fail ((parser semantic-debug-parser)) | |
527 | "Continue executiong in this PARSER until the next breakpoint." | |
528 | (setq semantic-debug-user-command 'fail) | |
529 | ) | |
530 | ||
531 | (defmethod semantic-debug-parser-quit ((parser semantic-debug-parser)) | |
532 | "Continue executiong in this PARSER until the next breakpoint." | |
533 | (setq semantic-debug-user-command 'quit) | |
534 | ) | |
535 | ||
536 | (defmethod semantic-debug-parser-abort ((parser semantic-debug-parser)) | |
537 | "Continue executiong in this PARSER until the next breakpoint." | |
538 | (setq semantic-debug-user-command 'abort) | |
539 | ) | |
540 | ||
541 | (defmethod semantic-debug-parser-print-state ((parser semantic-debug-parser)) | |
542 | "Print state for this PARSER at the current breakpoint." | |
543 | (with-slots (current-frame) semantic-debug-current-interface | |
544 | (when current-frame | |
545 | (semantic-debug-frame-info current-frame) | |
546 | ))) | |
547 | ||
548 | (defmethod semantic-debug-parser-break ((parser semantic-debug-parser)) | |
549 | "Set a breakpoint for this PARSER." | |
550 | ) | |
551 | ||
552 | ;; Stack stuff | |
553 | (defmethod semantic-debug-parser-frames ((parser semantic-debug-parser)) | |
554 | "Return a list of frames for the current parser. | |
555 | A frame is of the form: | |
556 | ( .. .what ? .. ) | |
557 | " | |
558 | (error "Parser has not implemented frame values") | |
559 | ) | |
560 | ||
561 | ||
562 | (provide 'semantic/debug) | |
563 | ||
6ca2fce3 | 564 | ;;; semantic/debug.el ends here |