;;; 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 (version 7.7.0 and 7.7.90 seem to work).

2. You also need the `netcat' program to create sockets.  MIT Scheme
   has some socket functions built-in, but I couldn't figure out how
   to access the locat port number of a server socket.  We shell out
   to netcat to get us started.

3. 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 
	     '(construct-normal-package-from-description
	       (make-package-description '(swank) '(()) 
					 (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)

   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)

(define (swank port)
  (accept-connections (or port 4005) #f))

(define (start-swank port-file)
  (accept-connections #f port-file))

;;;; Networking

(define (accept-connections port port-file)
  (let ((nc (netcat port)))
    (format #t "Listening on port: ~s~%" (cadr nc))
    (if port-file (write-port-file (cadr nc) port-file))
    (dynamic-wind 
	(lambda () #f)
	(lambda () (serve (netcat-accept (car nc))))
	(lambda () (close-port (subprocess-input-port (car nc)))))))

(define (netcat port)
  (let* ((sh (os/shell-file-name))
	 (cmd (format #f "exec netcat -s localhost -q 0 -l -v ~a 2>&1"
		      (if port (format #f "-p ~a" port) "")))
	 (netcat (start-pipe-subprocess sh 
					(vector sh "-c" cmd)
					scheme-subprocess-environment))
	 (line (read-line (subprocess-input-port netcat)))
	 (match (re-string-match "^listening on \\[[^]]+\\] \\([0-9]+\\) ...$"
				 line)))
    (cond ((not match)
	   (close-port (subprocess-input-port netcat))
	   (error "netcat:" line))
	  (else (list netcat 
		      (string->number (re-match-extract line match 1)))))))

(define (netcat-accept nc)
  (let* ((rx "^connect to \\[[^]]+\\] from [^ ]+ \\[[^]]+\\] \\([0-9]+\\)$")
	 (line (read-line (subprocess-input-port nc)))
	 (match (re-string-match rx line)))
    (cond ((not match) (error "netcat:" line))
	  (else (subprocess-input-port nc)))))

(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->ascii #\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)
  (or (name->package '(swank))
      (name->package '(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))))

(define (emacs-rex socket level sexp package thread id)
  (let ((ok? #f) (result #f))
    (dynamic-wind
	(lambda () #f)
	(lambda ()
	  (bind-condition-handler 
	   (list condition-type:serious-condition)
	   (lambda (c) (invoke-sldb socket (1+ level) c))
	   (lambda ()
	     (fluid-let ((*buffer-package* package))
	       (set! result 
		     (eval (cons* (car sexp) socket (cdr sexp))
			   swank-env))
	       (set! ok? #t)))))
	(lambda ()
	  (write-packet `(:return ,(if ok? `(:ok ,result) '(:abort))
				   ,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"))
      )))

(define (swank:quit-lisp _)
  (%exit))


;;;; Evaluation

(define (swank: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))


;;;; Compilation

(define (swank:compile-string-for-emacs _ string . x)
  (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 (format #f "~a" (internal-time/ticks->seconds time)))))

(define (swank:compiler-notes-for-emacs _) nil)

(define (swank:compile-file-for-emacs socket file load?)
  (call-compiler
   (lambda ()
     (with-output-to-repl socket
       (lambda () (compile-file file)))
     (cond ((elisp-true? load?)
	    (load (pathname-new-type file "com")
		  (user-env *buffer-package*)))))))

(define (swank:load-file socket file)
  (with-output-to-repl socket
    (lambda () (load file (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 ()
	      (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)

;; 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 string<?)
	  (longest-common-prefix strings))))

(define (all-completions pattern env match?)
  (let ((ss (map %symbol->string (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 <object>))
  (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 <pair>))
  (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 "(<parent>)" (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
  )

;;; swank-mit-scheme.scm ends here
