;;; swank-mit-scheme.scm --- SLIME server for MIT Scheme ;; ;; Copyright (C) 2008 Helmut Eller ;; ;; This file is licensed under the terms of the GNU General Public ;; License as distributed with Emacs (press C-h C-c for details). ;;;; Installation: #| 1. You need MIT Scheme 9.2 2. The Emacs side needs some fiddling. I have the following in my .emacs: (setq slime-lisp-implementations '((mit-scheme ("mit-scheme") :init mit-scheme-init))) (defun mit-scheme-init (file encoding) (format "%S\n\n" `(begin (load-option 'format) (load-option 'sos) (eval '(create-package-from-description (make-package-description '(swank) (list (list)) (vector) (vector) (vector) false)) (->environment '(package))) (load ,(expand-file-name ".../contrib/swank-mit-scheme.scm" ; <-- insert your path slime-path) (->environment '(swank))) (eval '(start-swank ,file) (->environment '(swank)))))) (defun mit-scheme () (interactive) (slime 'mit-scheme)) (defun find-mit-scheme-package () (save-excursion (let ((case-fold-search t)) (and (re-search-backward "^[;]+ package: \\((.+)\\).*$" nil t) (match-string-no-properties 1))))) (setq slime-find-buffer-package-function 'find-mit-scheme-package) (add-hook 'scheme-mode-hook (lambda () (slime-mode 1))) The `mit-scheme-init' function first loads the SOS and FORMAT libraries, then creates a package "(swank)", and loads this file into that package. Finally it starts the server. `find-mit-scheme-package' tries to figure out which package the buffer belongs to, assuming that ";;; package: (FOO)" appears somewhere in the file. Luckily, this assumption is true for many of MIT Scheme's own files. Alternatively, you could add Emacs style -*- slime-buffer-package: "(FOO)" -*- file variables. 4. Start everything with `M-x mit-scheme'. |# ;;; package: (swank) ;; Modified for Slimv: ;; - load options ;; - remove extension in compile-file-for-emacs (load-option 'format) (load-option 'sos) (if (< (car (get-subsystem-version "Release")) '9) (error "This file requires MIT Scheme Release 9")) (define (swank port) (accept-connections (or port 4005) #f)) ;; ### hardcoded port number for now. netcat-openbsd doesn't print ;; the listener port anymore. (define (start-swank port-file) (accept-connections 4055 port-file) ) ;;;; Networking (define (accept-connections port port-file) (let ((sock (open-tcp-server-socket port (host-address-loopback)))) (format #t "Listening on port: ~s~%" port) (if port-file (write-port-file port port-file)) (dynamic-wind (lambda () #f) (lambda () (serve (tcp-server-connection-accept sock #t #f))) (lambda () (close-tcp-server-socket sock))))) (define (write-port-file portnumber filename) (call-with-output-file filename (lambda (p) (write portnumber p)))) (define *top-level-restart* #f) (define (serve socket) (with-simple-restart 'disconnect "Close connection." (lambda () (with-keyboard-interrupt-handler (lambda () (main-loop socket)))))) (define (disconnect) (format #t "Disconnecting ...~%") (invoke-restart (find-restart 'disconnect))) (define (main-loop socket) (do () (#f) (with-simple-restart 'abort "Return to SLIME top-level." (lambda () (fluid-let ((*top-level-restart* (find-restart 'abort))) (dispatch (read-packet socket) socket 0)))))) (define (with-keyboard-interrupt-handler fun) (define (set-^G-handler exp) (eval `(vector-set! keyboard-interrupt-vector (char->integer #\G) ,exp) (->environment '(runtime interrupt-handler)))) (dynamic-wind (lambda () #f) (lambda () (set-^G-handler `(lambda (char) (with-simple-restart 'continue "Continue from interrupt." (lambda () (error "Keyboard Interrupt."))))) (fun)) (lambda () (set-^G-handler '^G-interrupt-handler)))) ;;;; Reading/Writing of SLIME packets (define (read-packet in) "Read an S-expression from STREAM using the SLIME protocol." (let* ((len (read-length in)) (buffer (make-string len))) (fill-buffer! in buffer) (read-from-string buffer))) (define (write-packet message out) (let* ((string (write-to-string message))) (log-event "WRITE: [~a]~s~%" (string-length string) string) (write-length (string-length string) out) (write-string string out) (flush-output out))) (define (fill-buffer! in buffer) (read-string! buffer in)) (define (read-length in) (if (eof-object? (peek-char in)) (disconnect)) (do ((len 6 (1- len)) (sum 0 (+ (* sum 16) (char->hex-digit (read-char in))))) ((zero? len) sum))) (define (ldb size position integer) "LoaD a Byte of SIZE bits at bit position POSITION from INTEGER." (fix:and (fix:lsh integer (- position)) (1- (fix:lsh 1 size)))) (define (write-length len out) (do ((pos 20 (- pos 4))) ((< pos 0)) (write-hex-digit (ldb 4 pos len) out))) (define (write-hex-digit n out) (write-char (hex-digit->char n) out)) (define (hex-digit->char n) (digit->char n 16)) (define (char->hex-digit c) (char->digit c 16)) ;;;; Event dispatching (define (dispatch request socket level) (log-event "READ: ~s~%" request) (case (car request) ((:emacs-rex) (apply emacs-rex socket level (cdr request))))) (define (swank-package) (if (name->package '(swank)) '(swank) '(user))) (define *buffer-package* #f) (define (find-buffer-package name) (if (elisp-false? name) #f (let ((v (ignore-errors (lambda () (name->package (read-from-string name)))))) (and (package? v) v)))) (define swank-env (->environment (swank-package))) (define (user-env buffer-package) (cond ((string? buffer-package) (let ((p (find-buffer-package buffer-package))) (if (not p) (error "Invalid package name: " buffer-package)) (package/environment p))) (else (nearest-repl/environment)))) ;; quote keywords (define (hack-quotes list) (map (lambda (x) (cond ((symbol? x) `(quote ,x)) (#t x))) list)) (define (emacs-rex socket level sexp package thread id) (let ((ok? #f) (result #f) (condition #f)) (dynamic-wind (lambda () #f) (lambda () (bind-condition-handler (list condition-type:serious-condition) (lambda (c) (set! condition c) (invoke-sldb socket (1+ level) c)) (lambda () (fluid-let ((*buffer-package* package)) (set! result (eval (cons* (car sexp) socket (hack-quotes (cdr sexp))) swank-env)) (set! ok? #t))))) (lambda () (write-packet `(:return ,(if ok? `(:ok ,result) `(:abort ,(if condition (format #f "~a" (condition/type condition)) ""))) ,id) socket))))) (define (swank:connection-info _) (let ((p (environment->package (user-env #f)))) `(:pid ,(unix/current-pid) :package (:name ,(write-to-string (package/name p)) :prompt ,(write-to-string (package/name p))) :lisp-implementation (:type "MIT Scheme" :version ,(get-subsystem-version-string "release")) :encoding (:coding-systems ("iso-8859-1")) ))) (define (swank:quit-lisp _) (%exit)) ;;;; Evaluation (define (swank-repl:listener-eval socket string) ;;(call-with-values (lambda () (eval-region string socket)) ;; (lambda values `(:values . ,(map write-to-string values)))) `(:values ,(write-to-string (eval-region string socket)))) (define (eval-region string socket) (let ((sexp (read-from-string string))) (if (eof-object? exp) (values) (with-output-to-repl socket (lambda () (eval sexp (user-env *buffer-package*))))))) (define (with-output-to-repl socket fun) (let ((p (make-port repl-port-type socket))) (dynamic-wind (lambda () #f) (lambda () (with-output-to-port p fun)) (lambda () (flush-output p))))) (define (swank:interactive-eval socket string) ;;(call-with-values (lambda () (eval-region string)) format-for-echo-area) (format-values (eval-region string socket)) ) (define (format-values . values) (if (null? values) "; No value" (with-string-output-port (lambda (out) (write-string "=> " out) (do ((vs values (cdr vs))) ((null? vs)) (write (car vs) out) (if (not (null? (cdr vs))) (write-string ", " out))))))) (define (swank:pprint-eval _ string) (pprint-to-string (eval (read-from-string string) (user-env *buffer-package*)))) (define (swank:interactive-eval-region socket string) (format-values (eval-region string socket))) (define (swank:set-package _ package) (set-repl/environment! (nearest-repl) (->environment (read-from-string package))) (let* ((p (environment->package (user-env #f))) (n (write-to-string (package/name p)))) (list n n))) (define (repl-write-substring port string start end) (cond ((< start end) (write-packet `(:write-string ,(substring string start end)) (port/state port)))) (- end start)) (define (repl-write-char port char) (write-packet `(:write-string ,(string char)) (port/state port))) (define repl-port-type (make-port-type `((write-substring ,repl-write-substring) (write-char ,repl-write-char)) #f)) (define (swank-repl:create-repl socket . _) (let* ((env (user-env #f)) (name (format #f "~a" (package/name (environment->package env))))) (list name name))) ;;;; Compilation (define (swank:compile-string-for-emacs _ string . x) (apply (lambda (errors seconds) `(:compilation-result ,errors t ,seconds nil nil)) (call-compiler (lambda () (let* ((sexps (snarf-string string)) (env (user-env *buffer-package*)) (scode (syntax `(begin ,@sexps) env)) (compiled-expression (compile-scode scode #t))) (scode-eval compiled-expression env)))))) (define (snarf-string string) (with-input-from-string string (lambda () (let loop () (let ((e (read))) (if (eof-object? e) '() (cons e (loop)))))))) (define (call-compiler fun) (let ((time #f)) (with-timings fun (lambda (run-time gc-time real-time) (set! time real-time))) (list 'nil (internal-time/ticks->seconds time)))) (define (swank:compiler-notes-for-emacs _) nil) (define (swank:compile-file-for-emacs socket file load?) (apply (lambda (errors seconds) (list ':compilation-result errors 't seconds load? (->namestring (pathname-name file)))) (call-compiler (lambda () (with-output-to-repl socket (lambda () (compile-file file))))))) (define (swank:load-file socket file) (with-output-to-repl socket (lambda () (pprint-to-string (load file (user-env *buffer-package*)))))) (define (swank:disassemble-form _ string) (let ((sexp (let ((sexp (read-from-string string))) (cond ((and (pair? sexp) (eq? (car sexp) 'quote)) (cadr sexp)) (#t sexp))))) (with-output-to-string (lambda () (compiler:disassemble (eval sexp (user-env *buffer-package*))))))) (define (swank:disassemble-symbol _ string) (with-output-to-string (lambda () (compiler:disassemble (eval (read-from-string string) (user-env *buffer-package*)))))) ;;;; Macroexpansion (define (swank:swank-macroexpand-all _ string) (with-output-to-string (lambda () (pp (syntax (read-from-string string) (user-env *buffer-package*)))))) (define swank:swank-macroexpand-1 swank:swank-macroexpand-all) (define swank:swank-macroexpand swank:swank-macroexpand-all) ;;; Arglist (define (swank:operator-arglist socket name pack) (let ((v (ignore-errors (lambda () (string-trim-right (with-output-to-string (lambda () (carefully-pa (eval (read-from-string name) (user-env pack)))))))))) (if (condition? v) 'nil v))) (define (carefully-pa o) (cond ((arity-dispatched-procedure? o) ;; MIT Scheme crashes for (pa /) (display "arity-dispatched-procedure")) ((procedure? o) (pa o)) (else (error "Not a procedure")))) ;;; Some unimplemented stuff. (define (swank:buffer-first-change . _) nil) (define (swank:filename-to-modulename . _) nil) (define (swank:swank-require . _) nil) ;; M-. is beyond my capabilities. (define (swank:find-definitions-for-emacs . _) nil) ;;; Debugger (define-structure (sldb-state (conc-name sldb-state.)) condition restarts) (define *sldb-state* #f) (define (invoke-sldb socket level condition) (fluid-let ((*sldb-state* (make-sldb-state condition (bound-restarts)))) (dynamic-wind (lambda () #f) (lambda () (write-packet `(:debug 0 ,level ,@(sldb-info *sldb-state* 0 20)) socket) (sldb-loop level socket)) (lambda () (write-packet `(:debug-return 0 ,level nil) socket))))) (define (sldb-loop level socket) (write-packet `(:debug-activate 0 ,level) socket) (with-simple-restart 'abort (format #f "Return to SLDB level ~a." level) (lambda () (dispatch (read-packet socket) socket level))) (sldb-loop level socket)) (define (sldb-info state start end) (let ((c (sldb-state.condition state)) (rs (sldb-state.restarts state))) (list (list (condition/report-string c) (format #f " [~a]" (%condition-type/name (condition/type c))) nil) (sldb-restarts rs) (sldb-backtrace c start end) ;;'((0 "dummy frame")) '()))) (define %condition-type/name (eval '%condition-type/name (->environment '(runtime error-handler)))) (define (sldb-restarts restarts) (map (lambda (r) (list (symbol->string (restart/name r)) (with-string-output-port (lambda (p) (write-restart-report r p))))) restarts)) (define (swank:throw-to-toplevel . _) (invoke-restart *top-level-restart*)) (define (swank:sldb-abort . _) (abort (sldb-state.restarts *sldb-state*))) (define (swank:sldb-continue . _) (continue (sldb-state.restarts *sldb-state*))) (define (swank:invoke-nth-restart-for-emacs _ _sldb-level n) (invoke-restart (list-ref (sldb-state.restarts *sldb-state*) n))) (define (swank:debugger-info-for-emacs _ from to) (sldb-info *sldb-state* from to)) (define (swank:backtrace _ from to) (sldb-backtrace (sldb-state.condition *sldb-state*) from to)) (define (sldb-backtrace condition from to) (sldb-backtrace-aux (condition/continuation condition) from to)) (define (sldb-backtrace-aux k from to) (let ((l (map frame>string (substream (continuation>frames k) from to)))) (let loop ((i from) (l l)) (if (null? l) '() (cons (list i (car l)) (loop (1+ i) (cdr l))))))) ;; Stack parser fails for this: ;; (map (lambda (x) x) "/tmp/x.x") (define (continuation>frames k) (let loop ((frame (continuation->stack-frame k))) (cond ((not frame) (stream)) (else (let ((next (ignore-errors (lambda () (stack-frame/next-subproblem frame))))) (cons-stream frame (if (condition? next) (stream next) (loop next)))))))) (define (frame>string frame) (if (condition? frame) (format #f "Bogus frame: ~a ~a" frame (condition/report-string frame)) (with-string-output-port (lambda (p) (print-frame frame p))))) (define (print-frame frame port) (define (invalid-subexpression? subexpression) (or (debugging-info/undefined-expression? subexpression) (debugging-info/unknown-expression? subexpression))) (define (invalid-expression? expression) (or (debugging-info/undefined-expression? expression) (debugging-info/compiled-code? expression))) (with-values (lambda () (stack-frame/debugging-info frame)) (lambda (expression environment subexpression) (cond ((debugging-info/compiled-code? expression) (write-string ";unknown compiled code" port)) ((not (debugging-info/undefined-expression? expression)) (fluid-let ((*unparse-primitives-by-name?* #t)) (write (unsyntax (if (invalid-subexpression? subexpression) expression subexpression)) port))) ((debugging-info/noise? expression) (write-string ";" port) (write-string ((debugging-info/noise expression) #f) port)) (else (write-string ";undefined expression" port)))))) (define (substream s from to) (let loop ((i 0) (l '()) (s s)) (cond ((or (= i to) (stream-null? s)) (reverse l)) ((< i from) (loop (1+ i) l (stream-cdr s))) (else (loop (1+ i) (cons (stream-car s) l) (stream-cdr s)))))) (define (swank:frame-locals-and-catch-tags _ frame) (list (map frame-var>elisp (frame-vars (sldb-get-frame frame))) '())) (define (frame-vars frame) (with-values (lambda () (stack-frame/debugging-info frame)) (lambda (expression environment subexpression) (cond ((environment? environment) (environment>frame-vars environment)) (else '()))))) (define (environment>frame-vars environment) (let loop ((e environment)) (cond ((environment->package e) '()) (else (append (environment-bindings e) (if (environment-has-parent? e) (loop (environment-parent e)) '())))))) (define (frame-var>elisp b) (list ':name (write-to-string (car b)) ':value (cond ((null? (cdr b)) "{unavailable}") (else (>line (cadr b)))) ':id 0)) (define (sldb-get-frame index) (stream-ref (continuation>frames (condition/continuation (sldb-state.condition *sldb-state*))) index)) (define (frame-var-value frame var) (let ((binding (list-ref (frame-vars frame) var))) (cond ((cdr binding) (cadr binding)) (else unspecific)))) (define (swank:inspect-frame-var _ frame var) (reset-inspector) (inspect-object (frame-var-value (sldb-get-frame frame) var))) ;;;; Completion (define (swank:simple-completions _ string package) (let ((strings (all-completions string (user-env package) string-prefix?))) (list (sort strings stringstring (environment-names env)))) (keep-matching-items ss (lambda (s) (match? pattern s))))) ;; symbol->string is too slow (define %symbol->string symbol-name) (define (environment-names env) (append (environment-bound-names env) (if (environment-has-parent? env) (environment-names (environment-parent env)) '()))) (define (longest-common-prefix strings) (define (common-prefix s1 s2) (substring s1 0 (string-match-forward s1 s2))) (reduce common-prefix "" strings)) ;;;; Apropos (define (swank:apropos-list-for-emacs _ name #!optional external-only case-sensitive package) (let* ((pkg (and (string? package) (find-package (read-from-string package)))) (parent (and (not (default-object? external-only)) (elisp-false? external-only))) (ss (append-map (lambda (p) (map (lambda (s) (cons p s)) (apropos-list name p (and pkg parent)))) (if pkg (list pkg) (all-packages)))) (ss (sublist ss 0 (min (length ss) 200)))) (map (lambda (e) (let ((p (car e)) (s (cdr e))) (list ':designator (format #f "~a ~a" s (package/name p)) ':variable (>line (ignore-errors (lambda () (package-lookup p s))))))) ss))) (define (swank:list-all-package-names . _) (map (lambda (p) (write-to-string (package/name p))) (all-packages))) (define (all-packages) (define (package-and-children package) (append (list package) (append-map package-and-children (package/children package)))) (package-and-children system-global-package)) ;;;; Inspector (define-structure (inspector-state (conc-name istate.)) object parts next previous content) (define istate #f) (define (reset-inspector) (set! istate #f)) (define (swank:init-inspector _ string) (reset-inspector) (inspect-object (eval (read-from-string string) (user-env *buffer-package*)))) (define (inspect-object o) (let ((previous istate) (content (inspect o)) (parts (make-eqv-hash-table))) (set! istate (make-inspector-state o parts #f previous content)) (if previous (set-istate.next! previous istate)) (istate>elisp istate))) (define (istate>elisp istate) (list ':title (>line (istate.object istate)) ':id (assign-index (istate.object istate) (istate.parts istate)) ':content (prepare-range (istate.parts istate) (istate.content istate) 0 500))) (define (assign-index o parts) (let ((i (hash-table/count parts))) (hash-table/put! parts i o) i)) (define (prepare-range parts content from to) (let* ((cs (substream content from to)) (ps (prepare-parts cs parts))) (list ps (if (< (length cs) (- to from)) (+ from (length cs)) (+ to 1000)) from to))) (define (prepare-parts ps parts) (define (line label value) `(,(format #f "~a: " label) (:value ,(>line value) ,(assign-index value parts)) "\n")) (append-map (lambda (p) (cond ((string? p) (list p)) ((symbol? p) (list (symbol->string p))) (#t (case (car p) ((line) (apply line (cdr p))) (else (error "Invalid part:" p)))))) ps)) (define (swank:inspect-nth-part _ index) (inspect-object (hash-table/get (istate.parts istate) index 'no-such-part))) (define (swank:quit-inspector _) (reset-inspector)) (define (swank:inspector-pop _) (cond ((istate.previous istate) (set! istate (istate.previous istate)) (istate>elisp istate)) (else 'nil))) (define (swank:inspector-next _) (cond ((istate.next istate) (set! istate (istate.next istate)) (istate>elisp istate)) (else 'nil))) (define (swank:inspector-range _ from to) (prepare-range (istate.parts istate) (istate.content istate) from to)) (define-syntax stream* (syntax-rules () ((stream* tail) tail) ((stream* e1 e2 ...) (cons-stream e1 (stream* e2 ...))))) (define (iline label value) `(line ,label ,value)) (define-generic inspect (o)) (define-method inspect ((o )) (cond ((environment? o) (inspect-environment o)) ((vector? o) (inspect-vector o)) ((procedure? o) (inspect-procedure o)) ((compiled-code-block? o) (inspect-code-block o)) ;;((system-pair? o) (inspect-system-pair o)) ((probably-scode? o) (inspect-scode o)) (else (inspect-fallback o)))) (define (inspect-fallback o) (let* ((class (object-class o)) (slots (class-slots class))) (stream* (iline "Class" class) (let loop ((slots slots)) (cond ((null? slots) (stream)) (else (let ((n (slot-name (car slots)))) (stream* (iline n (slot-value o n)) (loop (cdr slots)))))))))) (define-method inspect ((o )) (if (or (pair? (cdr o)) (null? (cdr o))) (inspect-list o) (inspect-cons o))) (define (inspect-cons o) (stream (iline "car" (car o)) (iline "cdr" (cdr o)))) (define (inspect-list o) (let loop ((i 0) (o o)) (cond ((null? o) (stream)) ((or (pair? (cdr o)) (null? (cdr o))) (stream* (iline i (car o)) (loop (1+ i) (cdr o)))) (else (stream (iline i (car o)) (iline "tail" (cdr o))))))) (define (inspect-environment o) (stream* (iline "(package)" (environment->package o)) (let loop ((bs (environment-bindings o))) (cond ((null? bs) (if (environment-has-parent? o) (stream (iline "()" (environment-parent o))) (stream))) (else (let* ((b (car bs)) (s (car b))) (cond ((null? (cdr b)) (stream* s " {" (environment-reference-type o s) "}\n" (loop (cdr bs)))) (else (stream* (iline s (cadr b)) (loop (cdr bs))))))))))) (define (inspect-vector o) (let ((len (vector-length o))) (let loop ((i 0)) (cond ((= i len) (stream)) (else (stream* (iline i (vector-ref o i)) (loop (1+ i)))))))) (define (inspect-procedure o) (cond ((primitive-procedure? o) (stream (iline "name" (primitive-procedure-name o)) (iline "arity" (primitive-procedure-arity o)) (iline "doc" (primitive-procedure-documentation o)))) ((compound-procedure? o) (stream (iline "arity" (procedure-arity o)) (iline "lambda" (procedure-lambda o)) (iline "env" (ignore-errors (lambda () (procedure-environment o)))))) (else (stream (iline "block" (compiled-entry/block o)) (with-output-to-string (lambda () (compiler:disassemble o))))))) (define (inspect-code-block o) (stream-append (let loop ((i (compiled-code-block/constants-start o))) (cond ((>= i (compiled-code-block/constants-end o)) (stream)) (else (stream* (iline i (system-vector-ref o i)) (loop (+ i compiled-code-block/bytes-per-object)))))) (stream (iline "debuginfo" (compiled-code-block/debugging-info o)) (iline "env" (compiled-code-block/environment o)) (with-output-to-string (lambda () (compiler:disassemble o)))))) (define (inspect-scode o) (stream (pprint-to-string o))) (define (probably-scode? o) (define tests (list access? assignment? combination? comment? conditional? definition? delay? disjunction? lambda? quotation? sequence? the-environment? variable?)) (let loop ((tests tests)) (cond ((null? tests) #f) (((car tests) o)) (else (loop (cdr tests)))))) (define (inspect-system-pair o) (stream (iline "car" (system-pair-car o)) (iline "cdr" (system-pair-cdr o)))) ;;;; Auxilary functions (define nil '()) (define t 't) (define (elisp-false? o) (member o '(nil ()))) (define (elisp-true? o) (not (elisp-false? o))) (define (>line o) (let ((r (write-to-string o 100))) (cond ((not (car r)) (cdr r)) (else (string-append (cdr r) " .."))))) ;; Must compile >line otherwise we can't write unassigend-reference-traps. (set! >line (compile-procedure >line)) (define (read-from-string s) (with-input-from-string s read)) (define (pprint-to-string o) (with-string-output-port (lambda (p) (fluid-let ((*unparser-list-breadth-limit* 10) (*unparser-list-depth-limit* 4) (*unparser-string-length-limit* 100)) (pp o p))))) ;(define (1+ n) (+ n 1)) (define (1- n) (- n 1)) (define (package-lookup package name) (let ((p (if (package? package) package (find-package package)))) (environment-lookup (package/environment p) name))) (define log-port (current-output-port)) (define (log-event fstring . args) ;;(apply format log-port fstring args) #f ) ;; Modified for Slimv: ;; - restart swank server in a loop (let loop () (swank 4005) (loop)) ;;; swank-mit-scheme.scm ends here