cdr commands)))))) (define* (display-group group #:optional (abbrev? #t)) (format #t "~:(~A~) Commands~:[~; [abbrev]~]:~2%" (group-name group) abbrev?) (for-each (lambda (c) (display-summary (command-usage c) (if abbrev? (command-abbrevs c) '()) (command-summary c))) (group-commands group)) (newline)) (define (display-command command) (display "Usage: ") (display (command-doc command)) (newline)) (define (display-summary usage abbrevs summary) (let* ((usage-len (string-length usage)) (abbrevs (if (pair? abbrevs) (format #f "[,~A~{ ,~A~}]" (car abbrevs) (cdr abbrevs)) "")) (abbrevs-len (string-length abbrevs))) (format #t " ,~A~A~A - ~A\n" usage (cond ((> abbrevs-len 32) (error "abbrevs too long" abbrevs)) ((> (+ usage-len abbrevs-len) 32) (format #f "~%~v_" (+ 2 (- 32 abbrevs-len)))) (else (format #f "~v_" (- 32 abbrevs-len usage-len)))) abbrevs summary))) (define (read-command repl) (catch #t (lambda () (read)) (lambda (key . args) (pmatch args ((,subr ,msg ,args . ,rest) (format #t "Throw to key `~a' while reading command:\n" key) (display-error #f (current-output-port) subr msg args rest)) (else (format #t "Throw to key `~a' with args `~s' while reading command.\n" key args))) (force-output) *unspecified*))) (define (read-command-arguments c repl) ((command-info-arguments-reader (command-info c)) repl)) (define (meta-command repl) (let ((command (read-command repl))) (cond ((eq? command *unspecified*)) ; read error, already signalled; pass. ((not (symbol? command)) (format #t "Meta-command not a symbol: ~s~%" command)) ((lookup-command command) => (lambda (c) (and=> (read-command-arguments c repl) (lambda (args) (apply (command-procedure c) repl args))))) (else (format #t "Unknown meta command: ~A~%" command))))) (define (add-meta-command! name category proc argument-reader) (hashq-set! *command-infos* name (make-command-info proc argument-reader)) (if category (let ((entry (assq category *command-table*))) (if entry (set-cdr! entry (append (cdr entry) (list (list name)))) (set! *command-table* (append *command-table* (list (list category (list name))))))))) (define-syntax define-meta-command (syntax-rules () ((_ ((name category) repl (expression0 ...) . datums) docstring b0 b1 ...) (add-meta-command! 'name 'category (lambda* (repl expression0 ... . datums) docstring b0 b1 ...) (lambda (repl) (define (handle-read-error form-name key args) (pmatch args ((,subr ,msg ,args . ,rest) (format #t "Throw to key `~a' while reading ~@[argument `~A' of ~]command `~A':\n" key form-name 'name) (display-error #f (current-output-port) subr msg args rest)) (else (format #t "Throw to key `~a' with args `~s' while reading ~@[ argument `~A' of ~]command `~A'.\n" key args form-name 'name))) (abort)) (% (let* ((expression0 (catch #t (lambda () (repl-reader "" (lambda* (#:optional (port (current-input-port))) ((language-reader (repl-language repl)) port (current-module))))) (lambda (k . args) (handle-read-error 'expression0 k args)))) ...) (append (list expression0 ...) (catch #t (lambda () (let ((port (open-input-string (read-line)))) (let lp ((out '())) (let ((x (read port))) (if (eof-object? x) (reverse out) (lp (cons x out))))))) (lambda (k . args) (handle-read-error #f k args))))) (lambda (k) #f))))) ; the abort handler ((_ ((name category) repl . datums) docstring b0 b1 ...) (define-meta-command ((name category) repl () . datums) docstring b0 b1 ...)) ((_ (name repl (expression0 ...) . datums) docstring b0 b1 ...) (define-meta-command ((name #f) repl (expression0 ...) . datums) docstring b0 b1 ...)) ((_ (name repl . datums) docstring b0 b1 ...) (define-meta-command ((name #f) repl () . datums) docstring b0 b1 ...))))