1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-20 10:40:19 +02:00

New files for Guile Emacs support.

This commit is contained in:
Keisuke Nishida 2001-04-25 12:15:24 +00:00
parent 253081cf6a
commit 2d857fb1ac
6 changed files with 741 additions and 0 deletions

View file

@ -1,3 +1,7 @@
2001-04-25 Keisuke Nishida <kxn30@po.cwru.edu>
* guile.el, guile-scheme.el, guile-emacs.scm: New files.
2001-03-13 Martin Grabmueller <mgrabmue@cs.tu-berlin.de> 2001-03-13 Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
* guile-c.el (guile-c-edit-docstring): Set fill-column to 63, so * guile-c.el (guile-c-edit-docstring): Set fill-column to 63, so

127
emacs/guile-emacs.scm Normal file
View file

@ -0,0 +1,127 @@
;;; guile-emacs.scm --- Guile Emacs interface
;; Copyright (C) 2001 Keisuke Nishida <kxn30@po.cwru.edu>
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Code:
(use-modules (ice-9 regex))
(use-modules (ice-9 channel))
(use-modules (ice-9 session))
(use-modules (ice-9 documentation))
;;;
;;; Emacs Lisp channel
;;;
(define (emacs-lisp-channel)
(define (native-type? x)
(or (integer? x) (symbol? x) (string? x) (pair? x) (vector? x)))
(define (emacs-lisp-print ch val)
(cond
((unspecified? val))
((eq? val #t) (channel-print-value ch 't))
((or (eq? val #f) (null? val)) (channel-print-value ch 'nil))
((native-type? val) (channel-print-value ch val))
(else (channel-print-token ch val))))
(channel-open (make-object-channel emacs-lisp-print)))
;;;
;;; Scheme channel
;;;
(define (emacs-scheme-channel)
(define (print ch val) (channel-print-value ch (object->string val)))
(channel-open (make-object-channel print)))
;;;
;;; for guile-import and guile-use-modules
;;;
(define (guile-emacs-export-procedure proc)
(define (procedure-arity proc)
(assq-ref (procedure-properties proc) 'arity))
(define (procedure-args proc)
(let ((source (procedure-source proc)))
(if source
;; formals -> emacs args
(let loop ((formals (cadr source)))
(cond
((null? formals) '())
((symbol? formals) `(&rest ,formals))
(else (cons (car formals) (loop (cdr formals))))))
;; arity -> emacs args
(let* ((arity (procedure-arity proc))
(nreqs (car arity))
(nopts (cadr arity))
(restp (caddr arity)))
(define (nsyms n)
(if (= n 0) '() (cons (gensym "a") (nsyms (1- n)))))
(append! (nsyms nreqs)
(if (> nopts 0) (cons '&optional (nsyms nopts)) '())
(if restp (cons '&rest (nsyms 1)) '()))))))
(define (procedure-call name args)
(let ((restp (memq '&rest args))
(args (delq '&rest (delq '&optional args))))
(if restp
`(list* ',name ,@args)
`(list ',name ,@args))))
(let ((name (procedure-name proc))
(args (procedure-args proc))
(docs (object-documentation proc)))
`(defun ,name ,args
,@(if docs (list docs) '())
(guile-lisp-eval ,(procedure-call name args)))))
(define (guile-emacs-export proc-name)
(guile-emacs-export-procedure (module-ref (current-module) proc-name)))
(define (guile-emacs-export-procedures module-name)
(define (module-public-procedures name)
(hash-fold (lambda (s v d)
(let ((val (variable-ref v)))
(if (procedure? val) (cons val d) d)))
'() (module-obarray (resolve-interface name))))
`(progn ,@(map guile-emacs-export-procedure
(module-public-procedures module-name))))
;;;
;;; for guile-emacs-complete-symbol
;;;
(define (guile-emacs-complete-alist str)
(sort! (apropos-fold (lambda (module name val data)
(cons (list (symbol->string name)
(cond ((procedure? val) " <p>")
((macro? val) " <m>")
(else "")))
data))
'() (string-append "^" (regexp-quote str))
apropos-fold-all)
(lambda (p1 p2) (string<? (car p1) (car p2)))))
;;; guile-emacs.scm ends here

334
emacs/guile-scheme.el Normal file
View file

@ -0,0 +1,334 @@
;;; guile-scheme.el --- Guile Scheme editing mode
;; Copyright (C) 2001 Free Software Foundation, Inc.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; Put the following lines in your ~/.emacs:
;;
;; (require 'guile-scheme)
;; (setq initial-major-mode 'scheme-interaction-mode)
;;; Code:
(require 'guile)
(require 'scheme)
(defgroup guile-scheme nil
"Editing Guile-Scheme code"
:group 'lisp)
(defvar guile-scheme-syntax-keywords
'((begin 0) (if 1) (cond 0) (case 1) (do 2)
quote syntax lambda and or else delay receive use-modules
(match 1) (match-lambda 0) (match-lambda* 0)
(let scheme-let-indent) (let* 1) (letrec 1) (and-let* 1)
(let-syntax 1) (letrec-syntax 1) (syntax-rules 1) (syntax-case 2)))
(defvar guile-scheme-special-procedures
'((catch 1) (lazy-catch 1) (stack-catch 1)
map for-each (dynamic-wind 3)))
;; set indent functions
(dolist (x (append guile-scheme-syntax-keywords
guile-scheme-special-procedures))
(when (consp x)
(put (car x) 'scheme-indent-function (cadr x))))
(defconst guile-scheme-font-lock-keywords
(eval-when-compile
(list
(list (concat "(\\(define\\*?\\("
;; Function names.
"\\(\\|-public\\|-method\\|-generic\\)\\|"
;; Macro names, as variable names.
"\\(-syntax\\|-macro\\)\\|"
;; Others
"-\\sw+\\)\\)\\>"
;; Any whitespace and declared object.
"\\s *(?\\(\\sw+\\)?")
'(1 font-lock-keyword-face)
'(5 (cond ((match-beginning 3) font-lock-function-name-face)
((match-beginning 4) font-lock-variable-name-face)
(t font-lock-type-face)) nil t))
(list (concat
"(" (regexp-opt
(mapcar (lambda (e)
(prin1-to-string (if (consp e) (car e) e)))
(append guile-scheme-syntax-keywords
guile-scheme-special-procedures)) 'words))
'(1 font-lock-keyword-face))
'("<\\sw+>" . font-lock-type-face)
'("\\<:\\sw+\\>" . font-lock-builtin-face)
))
"Expressions to highlight in Guile Scheme mode.")
;;;
;;; Guile Scheme mode
;;;
(defvar guile-scheme-mode-map nil
"Keymap for Guile Scheme mode.
All commands in `lisp-mode-shared-map' are inherited by this map.")
(unless guile-scheme-mode-map
(let ((map (make-sparse-keymap "Guile-Scheme")))
(setq guile-scheme-mode-map map)
(set-keymap-parent map lisp-mode-shared-map)
(define-key map [menu-bar] (make-sparse-keymap))
(define-key map [menu-bar guile-scheme] (cons "Guile-Scheme" map))
(define-key map [uncomment-region]
'("Uncomment Out Region" . (lambda (beg end)
(interactive "r")
(comment-region beg end '(4)))))
(define-key map [comment-region] '("Comment Out Region" . comment-region))
(define-key map [indent-region] '("Indent Region" . indent-region))
(define-key map [indent-line] '("Indent Line" . lisp-indent-line))
(define-key map "\e\C-i" 'guile-scheme-complete-symbol)
(define-key map "\e\C-x" 'guile-scheme-eval-define)
(define-key map "\C-x\C-e" 'guile-scheme-eval-last-sexp)
(define-key map "\C-c\C-b" 'guile-scheme-eval-buffer)
(define-key map "\C-c\C-r" 'guile-scheme-eval-region)
(define-key map "\C-c:" 'guile-scheme-eval-expression)
(define-key map "\C-c\C-a" 'guile-scheme-apropos)
(define-key map "\C-c\C-d" 'guile-scheme-describe)
(put 'comment-region 'menu-enable 'mark-active)
(put 'uncomment-region 'menu-enable 'mark-active)
(put 'indent-region 'menu-enable 'mark-active)))
(defcustom guile-scheme-mode-hook nil
"Normal hook run when entering `guile-scheme-mode'."
:type 'hook
:group 'guile-scheme)
;;;###autoload
(defun guile-scheme-mode ()
"Major mode for editing Guile Scheme code.
Editing commands are similar to those of `scheme-mode'.
\\{scheme-mode-map}
Entry to this mode calls the value of `scheme-mode-hook'
if that value is non-nil."
(interactive)
(kill-all-local-variables)
(setq mode-name "Guile Scheme")
(setq major-mode 'guile-scheme-mode)
(use-local-map guile-scheme-mode-map)
(scheme-mode-variables)
(setq mode-line-process
'(:eval (if (processp guile-scheme-adapter)
(format " [%s]" guile-scheme-command)
"")))
(setq font-lock-defaults
'((guile-scheme-font-lock-keywords)
nil t (("+-*/.<>=!?$%_&~^:@" . "w")) beginning-of-defun
(font-lock-mark-block-function . mark-defun)))
(run-hooks 'guile-scheme-mode-hook))
;;;
;;; Scheme interaction mode
;;;
(defvar scheme-interaction-mode-map ()
"Keymap for Scheme Interaction mode.
All commands in `guile-scheme-mode-map' are inherited by this map.")
(unless scheme-interaction-mode-map
(let ((map (make-sparse-keymap)))
(setq scheme-interaction-mode-map map)
(set-keymap-parent map guile-scheme-mode-map)
(define-key map "\C-j" 'guile-scheme-eval-print-last-sexp)
))
(defvar scheme-interaction-mode-hook nil
"Normal hook run when entering `scheme-interaction-mode'.")
(defun scheme-interaction-mode ()
"Major mode for evaluating Scheme expressions with Guile.
\\{scheme-interaction-mode-map}"
(interactive)
(guile-scheme-mode)
(use-local-map scheme-interaction-mode-map)
(setq major-mode 'scheme-interaction-mode)
(setq mode-name "Scheme Interaction")
(run-hooks 'scheme-interaction-mode-hook))
;;;
;;; Guile Scheme adapter
;;;
(defvar guile-scheme-command "guile")
(defvar guile-scheme-adapter nil)
(defun guile-scheme-adapter ()
(if (and (processp guile-scheme-adapter)
(eq (process-status guile-scheme-adapter) 'run))
guile-scheme-adapter
(setq guile-scheme-adapter
(guile:make-adapter guile-scheme-command 'emacs-scheme-channel))))
(defun guile-scheme-set-module ()
"Set the current module based on buffer contents.
If there is a (define-module ...) form, evaluate it.
Otherwise, choose module (guile-user)."
(save-excursion
(guile:eval
(if (re-search-backward "^(define-module " nil t)
(let ((start (match-beginning 0)))
(goto-char start)
(forward-sexp)
(buffer-substring-no-properties start (point)))
"(define-module (emacs-user))")
(guile-scheme-adapter))))
(defun guile-scheme-eval-string (string)
(guile-scheme-set-module)
(guile:eval string (guile-scheme-adapter)))
(defun guile-scheme-display-result (value flag)
(if (string= value "#<unspecified>")
(setq value "done"))
(if flag
(insert value)
(message "%s" value)))
;;;
;;; Interactive commands
;;;
(defun guile-scheme-eval-expression (string)
"Evaluate the expression in STRING and show value in echo area."
(interactive "SGuile Scheme Eval: ")
(guile-scheme-display-result (guile-scheme-eval-string string) nil))
(defun guile-scheme-eval-region (start end)
"Evaluate the region as Guile Scheme code."
(interactive "r")
(guile-scheme-eval-expression (buffer-substring-no-properties start end)))
(defun guile-scheme-eval-buffer ()
"Evaluate the current buffer as Guile Scheme code."
(interactive)
(guile-scheme-eval-expression (buffer-string)))
(defun guile-scheme-eval-last-sexp (arg)
"Evaluate sexp before point; show value in echo area.
With argument, print output into current buffer."
(interactive "P")
(guile-scheme-display-result
(guile-scheme-eval-string
(buffer-substring-no-properties
(point) (save-excursion (backward-sexp) (point)))) arg))
(defun guile-scheme-eval-print-last-sexp ()
"Evaluate sexp before point; print value into current buffer."
(interactive)
(insert "\n")
(guile-scheme-eval-last-sexp t)
(insert "\n"))
(defun guile-scheme-eval-define ()
(interactive)
(guile-scheme-eval-region (save-excursion (end-of-defun) (point))
(save-excursion (beginning-of-defun) (point))))
(defun guile-scheme-load-file (file)
"Load a Guile Scheme file."
(interactive "fGuile Scheme load file: ")
(guile-scheme-eval-string (format "(load %s)" (expand-file-name file)))
(message "done"))
(defun guile-scheme-complete-symbol ()
(interactive)
(unless (boundp 'guile-emacs-complete-alist)
(guile-import guile-emacs-complete-alist))
(let* ((end (point))
(start (save-excursion (skip-syntax-backward "w_") (point)))
(pattern (buffer-substring-no-properties start end))
(alist (guile-emacs-complete-alist pattern)))
(goto-char end)
(let ((completion (try-completion pattern alist)))
(cond ((eq completion t))
((not completion)
(message "Can't find completion for \"%s\"" pattern)
(ding))
((not (string= pattern completion))
(delete-region start end)
(insert completion))
(t
(message "Making completion list...")
(with-output-to-temp-buffer "*Completions*"
(display-completion-list alist))
(message "Making completion list...done"))))))
;; (define-command (guile-scheme-apropos regexp)
;; (interactive "sGuile-Scheme apropos (regexp): ")
;; (guile-scheme-set-module)
;; (let ((old #^guile-scheme-output-buffer))
;; (dynamic-wind
;; (lambda () (set! #^guile-scheme-output-buffer #f))
;; (lambda ()
;; (with-output-to-temp-buffer "*Help*"
;; (lambda ()
;; (apropos regexp))))
;; (lambda () (set! #^guile-scheme-output-buffer old)))))
;;
;; (define (guile-scheme-input-symbol prompt)
;; (let* ((symbol (thing-at-point 'symbol))
;; (table (map (lambda (sym) (list (symbol->string sym)))
;; (apropos-list "")))
;; (default (if (assoc symbol table)
;; (string-append " (default " symbol ")")
;; "")))
;; (string->symbol (completing-read (string-append prompt default ": ")
;; table #f #t #f #f symbol))))
;;
;; (define-command (guile-scheme-describe symbol)
;; "Display the value and documentation of SYMBOL."
;; (interactive (list (guile-scheme-input-symbol "Describe Guile-Scheme variable")))
;; (guile-scheme-set-module)
;; (let ((old #^guile-scheme-output-buffer))
;; (dynamic-wind
;; (lambda () (set! #^guile-scheme-output-buffer #f))
;; (lambda ()
;; (begin-with-output-to-temp-buffer "*Help*"
;; (describe symbol)))
;; (lambda () (set! #^guile-scheme-output-buffer old)))))
;;
;; (define-command (guile-scheme-find-definition symbol)
;; (interactive (list (guile-scheme-input-symbol "Guile-Scheme find definition")))
;; (guile-scheme-set-module)
;; )
;;;
;;; Turn on guile-scheme-mode for .scm files by default.
;;;
(setq auto-mode-alist
(cons '("\\.scm\\'" . guile-scheme-mode) auto-mode-alist))
(provide 'guile-scheme)
;;; guile-scheme.el ends here

172
emacs/guile.el Normal file
View file

@ -0,0 +1,172 @@
;;; guile.el --- Emacs Guile interface
;; Copyright (C) 2001 Keisuke Nishida <kxn30@po.cwru.edu>
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Code:
;;;
;;; Low level interface
;;;
(defvar guile-token "<guile>")
(defvar gulie-emacs-file
(catch 'return
(mapc (lambda (dir)
(let ((file (expand-file-name "guile-emacs.scm" dir)))
(if (file-exists-p file) (throw 'return file))))
load-path)
(error "Cannot find guile-emacs.scm")))
(defun guile:make-adapter (command channel)
(let* ((buff (generate-new-buffer " *guile object channel*"))
(proc (start-process "guile-oa" buff command
"-q" "-l" gulie-emacs-file)))
(process-kill-without-query proc)
(accept-process-output proc)
(guile-process-require proc (format "(%s)\n" channel) "channel> ")
proc))
(put 'guile-error 'error-conditions '(guile-error error))
(put 'guile-error 'error-message "Guile error")
(defun guile:eval (string adapter)
(let ((output (guile-process-require adapter (concat "eval " string "\n")
"channel> ")))
(cond
((string= output "") nil)
((string-match "^\\(\\(value\\)\\|\\(token\\)\\|\\(exception\\)\\) = "
output)
(cond
;; value
((match-beginning 2)
(car (read-from-string (substring output (match-end 0)))))
;; token
((match-beginning 3)
(cons guile-token
(car (read-from-string (substring output (match-end 0))))))
;; exception
((match-beginning 4)
(signal 'guile-error
(car (read-from-string (substring output (match-end 0))))))))
(t
(error "Unsupported result" output)))))
;;;
;;; Guile Lisp adapter
;;;
(defvar guile-lisp-command "guile")
(defvar guile-lisp-adapter nil)
(defvar true "#t")
(defvar false "#f")
(defun guile-lisp-adapter ()
(if (and (processp guile-lisp-adapter)
(eq (process-status guile-lisp-adapter) 'run))
guile-lisp-adapter
(setq guile-lisp-adapter
(guile:make-adapter guile-lisp-command 'emacs-lisp-channel))))
(defun guile-lisp-convert (x)
(cond
((or (eq x true) (eq x false)) x)
((stringp x) (prin1-to-string x))
((consp x)
(if (eq (car x) guile-token)
(cadr x)
(cons (guile-lisp-convert (car x)) (guile-lisp-convert (cdr x)))))
(t x)))
(defun guile-lisp-eval (exp)
(guile:eval (format "%s" (guile-lisp-convert exp)) (guile-lisp-adapter)))
;;;###autoload
(defmacro guile-import (name)
`(guile-process-import ',name))
(defun guile-process-import (name)
(eval (guile-lisp-eval `(guile-emacs-export ',name))))
;;;###autoload
(defmacro guile-use-modules (&rest name-list)
`(guile-process-use-modules ',name-list))
(defun guile-process-use-modules (list)
(unless (boundp 'guile-emacs-export-procedures)
(guile-import guile-emacs-export-procedures))
(guile-lisp-eval `(use-modules ,@list))
(mapc (lambda (name) (eval (guile-emacs-export-procedures name))) list))
;;;
;;; Process handling
;;;
(defvar guile-process-output-start nil)
(defvar guile-process-output-value nil)
(defvar guile-process-output-finished nil)
(defvar guile-process-output-separator nil)
(defun guile-process-require (process string separator)
(setq guile-process-output-value nil)
(setq guile-process-output-finished nil)
(setq guile-process-output-separator separator)
(let (temp-buffer)
(unless (process-buffer process)
(setq temp-buffer (guile-temp-buffer))
(set-process-buffer process temp-buffer))
(with-current-buffer (process-buffer process)
(goto-char (point-max))
(insert string)
(setq guile-process-output-start (point))
(set-process-filter process 'guile-process-filter)
(process-send-string process string)
(while (not guile-process-output-finished)
(unless (accept-process-output process 3)
(when (> (point) guile-process-output-start)
(display-buffer (current-buffer))
(error "BUG in Guile object channel!!")))))
(when temp-buffer
(set-process-buffer process nil)
(kill-buffer temp-buffer)))
guile-process-output-value)
(defun guile-process-filter (process string)
(with-current-buffer (process-buffer process)
(insert string)
(forward-line -1)
(if (< (point) guile-process-output-start)
(goto-char guile-process-output-start))
(when (re-search-forward guile-process-output-separator nil 0)
(goto-char (match-beginning 0))
(setq guile-process-output-value
(buffer-substring guile-process-output-start (point)))
(setq guile-process-output-finished t))))
(defun guile-process-kill (process)
(set-process-filter process nil)
(delete-process process)
(if (process-buffer process)
(kill-buffer (process-buffer process))))
(provide 'guile)
;;; guile.el ends here

View file

@ -1,3 +1,7 @@
2001-04-25 Keisuke Nishida <kxn30@po.cwru.edu>
* channel.scm: New file.
2001-04-19 Keisuke Nishida <kxn30@po.cwru.edu> 2001-04-19 Keisuke Nishida <kxn30@po.cwru.edu>
* receive.scm (receive): Use `define-macro'. * receive.scm (receive): Use `define-macro'.

100
ice-9/channel.scm Normal file
View file

@ -0,0 +1,100 @@
;;; Guile object channel
;; Copyright (C) 2001 Free Software Foundation, Inc.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Code:
(define-module (ice-9 channel)
:export (make-object-channel
channel-open channel-print-value channel-print-token))
;;;
;;; Channel type
;;;
(define channel-type
(make-record-type 'channel '(stdin stdout printer token-module)))
(define make-channel (record-constructor channel-type))
(define (make-object-channel printer)
(make-channel (current-input-port)
(current-output-port)
printer
(make-module)))
(define channel-stdin (record-accessor channel-type 'stdin))
(define channel-stdout (record-accessor channel-type 'stdout))
(define channel-printer (record-accessor channel-type 'printer))
(define channel-token-module (record-accessor channel-type 'token-module))
;;;
;;; Channel
;;;
(define (channel-open ch)
(let ((stdin (channel-stdin ch))
(stdout (channel-stdout ch))
(printer (channel-printer ch))
(token-module (channel-token-module ch)))
(let loop ()
(catch #t
(lambda ()
(channel:prompt stdout)
(let ((cmd (read stdin)))
(if (eof-object? cmd)
(throw 'quit)
(case cmd
((eval)
(module-use! (current-module) token-module)
(printer ch (eval (read stdin) (current-module))))
((destroy)
(let ((token (read stdin)))
(if (module-defined? token-module token)
(module-remove! token-module token)
(channel:error stdout "Invalid token: ~S" token))))
((quit)
(throw 'quit))
(else
(channel:error stdout "Unknown command: ~S" cmd)))))
(loop))
(lambda (key . args)
(case key
((quit) (throw 'quit))
(else
(format stdout "exception = ~S\n"
(list key (apply format #f (cadr args) (caddr args))))
(loop))))))))
(define (channel-print-value ch val)
(format (channel-stdout ch) "value = ~S\n" val))
(define (channel-print-token ch val)
(let* ((token (symbol-append (gensym "%%") '%%))
(pair (cons token (object->string val))))
(format (channel-stdout ch) "token = ~S\n" pair)
(module-define! (channel-token-module ch) token val)))
(define (channel:prompt port)
(display "channel> " port)
(force-output port))
(define (channel:error port msg . args)
(display "ERROR: " port)
(apply format port msg args)
(newline port))