Incorporate ice-9-debugger-extensions properly
[bpt/guile.git] / module / ice-9 / debugger / command-loop.scm
CommitLineData
8ee7506b
NJ
1;;;; Guile Debugger command loop
2
cd5fea8d 3;;; Copyright (C) 1999, 2001, 2002, 2003, 2006 Free Software Foundation, Inc.
8ee7506b 4;;;
53befeb7
NJ
5;;;; This library is free software; you can redistribute it and/or
6;;;; modify it under the terms of the GNU Lesser General Public
7;;;; License as published by the Free Software Foundation; either
8;;;; version 3 of the License, or (at your option) any later version.
9;;;;
10;;;; This library is distributed in the hope that it will be useful,
11;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13;;;; Lesser General Public License for more details.
14;;;;
15;;;; You should have received a copy of the GNU Lesser General Public
16;;;; License along with this library; if not, write to the Free Software
17;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
8ee7506b
NJ
18
19(define-module (ice-9 debugger command-loop)
c614a00b 20 #:use-module ((ice-9 debugger commands) :prefix debugger:)
ba5f8bf4
NJ
21 #:use-module (ice-9 debugger)
22 #:use-module (ice-9 debugger state)
23 #:use-module (ice-9 debugging traps)
8ee7506b
NJ
24 #:export (debugger-command-loop
25 debugger-command-loop-error
26 debugger-command-loop-quit)
27 #:no-backtrace)
28
8ee7506b
NJ
29;;; {Interface used by (ice-9 debugger).}
30
31(define (debugger-command-loop state)
32 (read-and-dispatch-commands state (current-input-port)))
33
34(define (debugger-command-loop-error message)
35 (user-error message))
36
37(define (debugger-command-loop-quit)
38 (throw 'exit-debugger))
39
40;;; {Implementation.}
41
42(define debugger-prompt "debug> ")
43
44(define (debugger-handler key . args)
45 (case key
46 ((exit-debugger) #f)
47 ((signal)
48 ;; Restore stack
49 (fluid-set! the-last-stack (fluid-ref before-signal-stack))
50 (apply display-error #f (current-error-port) args))
51 (else
52 (display "Internal debugger error:\n")
53 (save-stack debugger-handler)
54 (apply throw key args)))
55 (throw 'exit-debugger)) ;Pop the stack
56
57(define (read-and-dispatch-commands state port)
58 (catch 'exit-debugger
59 (lambda ()
60 (lazy-catch #t
61 (lambda ()
62 (with-fluids ((last-command #f))
63 (let loop ()
64 (read-and-dispatch-command state port)
65 (loop))))
66 debugger-handler))
67 (lambda args
68 *unspecified*)))
69
9124ba8d
NJ
70(define set-readline-prompt! #f)
71
8ee7506b
NJ
72(define (read-and-dispatch-command state port)
73 (if (using-readline?)
9124ba8d
NJ
74 (begin
75 ;; Import set-readline-prompt! if we haven't already.
76 (or set-readline-prompt!
77 (set! set-readline-prompt!
78 (module-ref (resolve-module '(ice-9 readline))
79 'set-readline-prompt!)))
80 (set-readline-prompt! debugger-prompt debugger-prompt))
8ee7506b
NJ
81 (display debugger-prompt))
82 (force-output) ;This should not be necessary...
83 (let ((token (read-token port)))
84 (cond ((eof-object? token)
85 (throw 'exit-debugger))
86 ((not token)
87 (discard-rest-of-line port)
88 (catch-user-errors port (lambda () (run-last-command state))))
89 (else
90 (catch-user-errors port
91 (lambda ()
92 (dispatch-command token command-table state port)))))))
93
94(define (run-last-command state)
95 (let ((procedure (fluid-ref last-command)))
96 (if procedure
97 (procedure state))))
98
99(define (catch-user-errors port thunk)
100 (catch 'debugger-user-error
101 thunk
102 (lambda (key . objects)
103 (apply user-warning objects)
104 (discard-rest-of-line port))))
105
106(define last-command (make-fluid))
107
108(define (user-warning . objects)
109 (for-each (lambda (object)
110 (display object))
111 objects)
112 (newline))
113
114(define (user-error . objects)
115 (apply throw 'debugger-user-error objects))
116\f
117;;;; Command dispatch
118
119(define (dispatch-command string table state port)
120 (let ((value (command-table-value table string)))
121 (if value
122 (dispatch-command/value value state port)
123 (user-error "Unknown command: " string))))
124
125(define (dispatch-command/value value state port)
126 (cond ((command? value)
127 (dispatch-command/command value state port))
128 ((command-table? value)
129 (dispatch-command/table value state port))
130 ((list? value)
131 (dispatch-command/name value state port))
132 (else
133 (error "Unrecognized command-table value: " value))))
134
135(define (dispatch-command/command command state port)
136 (let ((procedure (command-procedure command))
137 (arguments ((command-parser command) port)))
138 (let ((procedure (lambda (state) (apply procedure state arguments))))
139 (warn-about-extra-args port)
140 (fluid-set! last-command procedure)
141 (procedure state))))
142
143(define (warn-about-extra-args port)
144 ;; **** modify this to show the arguments.
145 (let ((char (skip-whitespace port)))
146 (cond ((eof-object? char) #f)
147 ((char=? #\newline char) (read-char port))
148 (else
149 (user-warning "Extra arguments at end of line: "
150 (read-rest-of-line port))))))
151
152(define (dispatch-command/table table state port)
153 (let ((token (read-token port)))
154 (if (or (eof-object? token)
155 (not token))
156 (user-error "Command name too short.")
157 (dispatch-command token table state port))))
158
159(define (dispatch-command/name name state port)
160 (let ((value (lookup-command name)))
161 (cond ((not value)
162 (apply user-error "Unknown command name: " name))
163 ((command-table? value)
164 (apply user-error "Partial command name: " name))
165 (else
166 (dispatch-command/value value state port)))))
167\f
168;;;; Command definition
169
170(define (define-command name argument-template procedure)
171 (let ((name (canonicalize-command-name name)))
172 (add-command name
173 (make-command name
174 (argument-template->parser argument-template)
175 (procedure-documentation procedure)
176 procedure)
177 command-table)
178 name))
179
180(define (define-command-alias name1 name2)
181 (let ((name1 (canonicalize-command-name name1)))
182 (add-command name1 (canonicalize-command-name name2) command-table)
183 name1))
184\f
185(define (argument-template->parser template)
186 ;; Deliberately handles only cases that occur in "commands.scm".
187 (cond ((eq? 'tokens template)
188 (lambda (port)
189 (let loop ((tokens '()))
190 (let ((token (read-token port)))
191 (if (or (eof-object? token)
192 (not token))
193 (list (reverse! tokens))
194 (loop (cons token tokens)))))))
195 ((null? template)
196 (lambda (port)
197 '()))
198 ((and (pair? template)
199 (null? (cdr template))
200 (eq? 'object (car template)))
201 (lambda (port)
202 (list (read port))))
203 ((and (pair? template)
204 (equal? ''optional (car template))
205 (pair? (cdr template))
206 (null? (cddr template)))
207 (case (cadr template)
208 ((token)
209 (lambda (port)
210 (let ((token (read-token port)))
211 (if (or (eof-object? token)
212 (not token))
213 (list #f)
214 (list token)))))
215 ((exact-integer)
216 (lambda (port)
217 (list (parse-optional-exact-integer port))))
218 ((exact-nonnegative-integer)
219 (lambda (port)
220 (list (parse-optional-exact-nonnegative-integer port))))
221 ((object)
222 (lambda (port)
223 (list (parse-optional-object port))))
224 (else
225 (error "Malformed argument template: " template))))
226 (else
227 (error "Malformed argument template: " template))))
228
229(define (parse-optional-exact-integer port)
230 (let ((object (parse-optional-object port)))
231 (if (or (not object)
232 (and (integer? object)
233 (exact? object)))
234 object
235 (user-error "Argument not an exact integer: " object))))
236
237(define (parse-optional-exact-nonnegative-integer port)
238 (let ((object (parse-optional-object port)))
239 (if (or (not object)
240 (and (integer? object)
241 (exact? object)
242 (not (negative? object))))
243 object
244 (user-error "Argument not an exact non-negative integer: " object))))
245
246(define (parse-optional-object port)
247 (let ((terminator (skip-whitespace port)))
248 (if (or (eof-object? terminator)
249 (eq? #\newline terminator))
250 #f
251 (let ((object (read port)))
252 (if (eof-object? object)
253 #f
254 object)))))
255\f
256;;;; Command tables
257
258(define (lookup-command name)
259 (let loop ((table command-table) (strings name))
260 (let ((value (command-table-value table (car strings))))
261 (cond ((or (not value) (null? (cdr strings))) value)
262 ((command-table? value) (loop value (cdr strings)))
263 (else #f)))))
264
265(define (command-table-value table string)
266 (let ((entry (command-table-entry table string)))
267 (and entry
268 (caddr entry))))
269
270(define (command-table-entry table string)
271 (let loop ((entries (command-table-entries table)))
272 (and (not (null? entries))
273 (let ((entry (car entries)))
274 (if (and (<= (cadr entry)
275 (string-length string)
276 (string-length (car entry)))
277 (= (string-length string)
278 (match-strings (car entry) string)))
279 entry
280 (loop (cdr entries)))))))
281
282(define (match-strings s1 s2)
283 (let ((n (min (string-length s1) (string-length s2))))
284 (let loop ((i 0))
285 (cond ((= i n) i)
286 ((char=? (string-ref s1 i) (string-ref s2 i)) (loop (+ i 1)))
287 (else i)))))
288
289(define (write-command-name name)
290 (display (car name))
291 (for-each (lambda (string)
292 (write-char #\space)
293 (display string))
294 (cdr name)))
295\f
296(define (add-command name value table)
297 (let loop ((strings name) (table table))
298 (let ((entry
299 (or (let loop ((entries (command-table-entries table)))
300 (and (not (null? entries))
301 (if (string=? (car strings) (caar entries))
302 (car entries)
303 (loop (cdr entries)))))
304 (let ((entry (list (car strings) #f #f)))
305 (let ((entries
306 (let ((entries (command-table-entries table)))
307 (if (or (null? entries)
308 (string<? (car strings) (caar entries)))
309 (cons entry entries)
310 (begin
311 (let loop ((prev entries) (this (cdr entries)))
312 (if (or (null? this)
313 (string<? (car strings) (caar this)))
314 (set-cdr! prev (cons entry this))
315 (loop this (cdr this))))
316 entries)))))
317 (compute-string-abbreviations! entries)
318 (set-command-table-entries! table entries))
319 entry))))
320 (if (null? (cdr strings))
321 (set-car! (cddr entry) value)
322 (loop (cdr strings)
323 (if (command-table? (caddr entry))
324 (caddr entry)
325 (let ((table (make-command-table '())))
326 (set-car! (cddr entry) table)
327 table)))))))
328
329(define (canonicalize-command-name name)
330 (cond ((and (string? name)
331 (not (string-null? name)))
332 (list name))
333 ((let loop ((name name))
334 (and (pair? name)
335 (string? (car name))
336 (not (string-null? (car name)))
337 (or (null? (cdr name))
338 (loop (cdr name)))))
339 name)
340 (else
341 (error "Illegal command name: " name))))
342
343(define (compute-string-abbreviations! entries)
344 (let loop ((entries entries) (index 0))
345 (let ((groups '()))
346 (for-each
347 (lambda (entry)
348 (let* ((char (string-ref (car entry) index))
349 (group (assv char groups)))
350 (if group
351 (set-cdr! group (cons entry (cdr group)))
352 (set! groups
353 (cons (list char entry)
354 groups)))))
355 entries)
356 (for-each
357 (lambda (group)
358 (let ((index (+ index 1)))
359 (if (null? (cddr group))
360 (set-car! (cdadr group) index)
361 (loop (let ((entry
362 (let loop ((entries (cdr group)))
363 (and (not (null? entries))
364 (if (= index (string-length (caar entries)))
365 (car entries)
366 (loop (cdr entries)))))))
367 (if entry
368 (begin
369 (set-car! (cdr entry) index)
370 (delq entry (cdr group)))
371 (cdr group)))
372 index))))
373 groups))))
374\f
375;;;; Data structures
376
377(define command-table-rtd (make-record-type "command-table" '(entries)))
378(define make-command-table (record-constructor command-table-rtd '(entries)))
379(define command-table? (record-predicate command-table-rtd))
380(define command-table-entries (record-accessor command-table-rtd 'entries))
381(define set-command-table-entries!
382 (record-modifier command-table-rtd 'entries))
383
384(define command-rtd
385 (make-record-type "command"
386 '(name parser documentation procedure)))
387
388(define make-command
389 (record-constructor command-rtd
390 '(name parser documentation procedure)))
391
392(define command? (record-predicate command-rtd))
393(define command-name (record-accessor command-rtd 'name))
394(define command-parser (record-accessor command-rtd 'parser))
395(define command-documentation (record-accessor command-rtd 'documentation))
396(define command-procedure (record-accessor command-rtd 'procedure))
397\f
398;;;; Character parsing
399
400(define (read-token port)
401 (letrec
402 ((loop
403 (lambda (chars)
404 (let ((char (peek-char port)))
405 (cond ((eof-object? char)
406 (do-eof char chars))
407 ((char=? #\newline char)
408 (do-eot chars))
409 ((char-whitespace? char)
410 (do-eot chars))
411 ((char=? #\# char)
412 (read-char port)
413 (let ((terminator (skip-comment port)))
414 (if (eof-object? char)
415 (do-eof char chars)
416 (do-eot chars))))
417 (else
418 (read-char port)
419 (loop (cons char chars)))))))
420 (do-eof
421 (lambda (eof chars)
422 (if (null? chars)
423 eof
424 (do-eot chars))))
425 (do-eot
426 (lambda (chars)
427 (if (null? chars)
428 #f
429 (list->string (reverse! chars))))))
430 (skip-whitespace port)
431 (loop '())))
432
433(define (skip-whitespace port)
434 (let ((char (peek-char port)))
435 (cond ((or (eof-object? char)
436 (char=? #\newline char))
437 char)
438 ((char-whitespace? char)
439 (read-char port)
440 (skip-whitespace port))
441 ((char=? #\# char)
442 (read-char port)
443 (skip-comment port))
444 (else char))))
445
446(define (skip-comment port)
447 (let ((char (peek-char port)))
448 (if (or (eof-object? char)
449 (char=? #\newline char))
450 char
451 (begin
452 (read-char port)
453 (skip-comment port)))))
454
455(define (read-rest-of-line port)
456 (let loop ((chars '()))
457 (let ((char (read-char port)))
458 (if (or (eof-object? char)
459 (char=? #\newline char))
460 (list->string (reverse! chars))
461 (loop (cons char chars))))))
462
463(define (discard-rest-of-line port)
464 (let loop ()
465 (if (not (let ((char (read-char port)))
466 (or (eof-object? char)
467 (char=? #\newline char))))
468 (loop))))
469\f
470;;;; Commands
471
472(define command-table (make-command-table '()))
473
474(define-command "help" 'tokens
475 (lambda (state tokens)
476 "Type \"help\" followed by a command name for full documentation."
477 (let loop ((name (if (null? tokens) '("help") tokens)))
478 (let ((value (lookup-command name)))
479 (cond ((not value)
480 (write-command-name name)
481 (display " is not a known command name.")
482 (newline))
483 ((command? value)
484 (display (command-documentation value))
485 (newline)
486 (if (equal? '("help") (command-name value))
487 (begin
488 (display "Available commands are:")
489 (newline)
490 (for-each (lambda (entry)
491 (if (not (list? (caddr entry)))
492 (begin
493 (display " ")
494 (display (car entry))
495 (newline))))
496 (command-table-entries command-table)))))
497 ((command-table? value)
498 (display "The \"")
499 (write-command-name name)
500 (display "\" command requires a subcommand.")
501 (newline)
502 (display "Available subcommands are:")
503 (newline)
504 (for-each (lambda (entry)
505 (if (not (list? (caddr entry)))
506 (begin
507 (display " ")
508 (write-command-name name)
509 (write-char #\space)
510 (display (car entry))
511 (newline))))
512 (command-table-entries value)))
513 ((list? value)
514 (loop value))
515 (else
516 (error "Unknown value from lookup-command:" value)))))
517 state))
518
c614a00b 519(define-command "frame" '('optional exact-nonnegative-integer) debugger:frame)
8ee7506b 520
c614a00b 521(define-command "position" '() debugger:position)
8ee7506b 522
c614a00b 523(define-command "up" '('optional exact-integer) debugger:up)
8ee7506b 524
c614a00b 525(define-command "down" '('optional exact-integer) debugger:down)
8ee7506b 526\f
c614a00b 527(define-command "backtrace" '('optional exact-integer) debugger:backtrace)
8ee7506b 528
c614a00b 529(define-command "evaluate" '(object) debugger:evaluate)
8ee7506b 530
c614a00b 531(define-command '("info" "args") '() debugger:info-args)
8ee7506b 532
c614a00b 533(define-command '("info" "frame") '() debugger:info-frame)
8ee7506b
NJ
534
535(define-command "quit" '()
536 (lambda (state)
537 "Exit the debugger."
538 (debugger-command-loop-quit)))
539
540(define-command-alias "f" "frame")
541(define-command-alias '("info" "f") '("info" "frame"))
542(define-command-alias "bt" "backtrace")
543(define-command-alias "where" "backtrace")
544(define-command-alias "p" "evaluate")
545(define-command-alias '("info" "stack") "backtrace")
ba5f8bf4
NJ
546
547(define-command "continue" '() debugger:continue)
548
549(define-command "finish" '() debugger:finish)
550
551(define-command "step" '('optional exact-integer) debugger:step)
552
553(define-command "next" '('optional exact-integer) debugger:next)