git.fiddlerwoaroof.com
command-interface/command-utils.scm
4e987026
 ;;; command-interface/command-utils.scm
 
 ;;; These are utilities used by the command interface.
 
 ;;; These send output to the user
 
 ;;; This is used in emacs mode
 
 (define (say/em . args)
   (say1 args))
 
 ;;; This is for both ordinary text to emacs and output to the command interface
 
 (define (say . args)
   (say1 args))
 
 (define (say1 args)
   (apply (function format) (cons (current-output-port) args)))
 
 ;;; This is for non-emacs output
 
 (define (say/ne . args)
   (when (not *emacs-mode*)
      (say1 args)))
 
 
 ;;; These random utilities should be elsewhere
 
 ;;; This determines whether the current module is loaded & available.
 ;;; If the module is Main, an empty Main module is created.
 
 (define (cm-available?)
   (cond ((table-entry *modules* *current-mod*)
 	 '#t)
 	((eq? *current-mod* '|Main|)
 	 (make-empty-main)
 	 '#t)
 	(else
 	 '#f)))
 
 ;;; This creates a empty module named Main to use as a scratch pad.
 
 (define (make-empty-main)
   (compile/load "$PRELUDE/Prelude")
   (setf *unit* '|Main|)
   (setf *current-mod* '|Main|)
   (let ((mods (parse-from-string
 	       "module Main where {import Prelude}"
 	       (function parse-module-list)
 	       "foo")))
     ;;; This should generate no code at all so the returned code is ignored.
     (modules->lisp-code mods)
     (setf (table-entry *modules* *current-mod*) (car mods))
     (clear-extended-modules)))
 
 (define (eval-fragment eval?)
   (cond ((not (cm-available?))
 	 (say "~&Module ~A is not loaded.~%" *current-mod*)
 	 'error)
 	((memq *fragment-status* '(Compiled Saved))
 	 (when eval?
 	     (eval-module *extension-module*))
 	 'ok)
 	((eq? *fragment-status* 'Error)
 	 (say/ne "~&Errors exist in current fragment.~%")
 	 'error)
 	((string=? *current-string* "")
 	 (say/ne "~&Current extension is empty.~%")
 	 'error)
 	(else
 	 (let ((res (compile-fragment
 		     *current-mod* *current-string*
 		     *extension-file-name*)))
 	   (cond ((eq? res 'error)
 		  (setf *fragment-status* 'Error)
 		  (notify-error))
 		 (else
 		  (setf *extension-module* res)
 		  (setf *fragment-status* 'Compiled)
 		  (when eval?
 			(eval-module *extension-module*))))))))
 
 (define (set-current-file file)
   (cond ((null? file)
 	 '())
 	((null? (cdr file))
 	 (setf *remembered-file* (car file)))
 	(else
 	 (say "~&Invalid file spec ~s.~%" file)
 	 (funcall *abort-command*))))
 
 (define (select-current-mod mods)
   (when (pair? mods)
     (when (not (memq *current-mod* mods))
       (setf *current-mod* (car mods))
       (say/ne "~&Now in module ~A.~%" *current-mod*))))
 
 ;;;  Emacs mode stuff
 
 ;;; *** bogus alert!!!  This coercion may fail to produce a
 ;;; *** real character in some Lisps.
 
 (define *emacs-notify-char* (integer->char 1))
 
 (define (notify-ready)
   (when *emacs-mode*
      (say/em "~Ar" *emacs-notify-char*)
      (force-output (current-output-port))))
 
 (define (notify-input-request)
   (when *emacs-mode*
      (say/em "~Ai" *emacs-notify-char*)
      (force-output (current-output-port))))
 
 (define (notify-error)
   (when *emacs-mode*
      (say/em "~Ae" *emacs-notify-char*)
      (force-output (current-output-port))))
 
 (define (notify-printers printers)
   (notify-settings "p" printers))
 
 (define (notify-optimizers optimizers)
   (notify-settings "o" optimizers))
 
 (define (notify-settings flag values)
   (when *emacs-mode*
     (say/em "~A~A(" *emacs-notify-char* flag)
     (dolist (p values)
       (say/em " ~A" (string-downcase (symbol->string p))))
     (say/em ")~%")
     (force-output (current-output-port))))
 
 (define (notify-status-line str)
   (when *emacs-mode*
      (say/em "~As~A~%" *emacs-notify-char* str)
      (force-output (current-output-port))))
 
 ;;; These are used to drive the real compiler. 
 
 (define *compile/compile-cflags*
   (make cflags
 	(load-code?          '#t)
 	(compile-code?       '#t)
 	(write-code?         '#t)
 	(write-interface?    '#t)))
 
 
 (define (compile/compile file)
   (haskell-compile file *compile/compile-cflags*))
 
 
 (define *compile/load-cflags*
   (make cflags
 	(load-code?          '#t)
 	(compile-code?       '#f)
 	(write-code?         '#f)
 	(write-interface?    '#f)))
 
 (define (compile/load file)
   (haskell-compile file *compile/load-cflags*))
 
 
 ;;; Printer setting support
 
 (define (set-printers args mode)
   (set-switches *printers* (strings->syms args)
 		mode *all-printers* "printers"))
 
 (define (set-optimizers args mode)
   (set-switches *optimizers* (strings->syms args)
 		mode *all-optimizers* "optimizers"))
 
 (define (set-switches current new mode all name)
   (dolist (s new)
     (when (and (not (eq? s 'all)) (not (memq s all)))
       (signal-invalid-value s name all)))
   (let ((res (cond ((eq? mode '+)
 		    (set-union current new))
 		   ((eq? mode '-)
 		    (set-difference current new))
 		   ((eq? mode '=)
 		    (if (equal? new '(all))
 			all
 			new)))))
     res))
 
 (define (signal-invalid-value s name all)
   (recoverable-error 'invalid-value
     "~A is not one of the valid ~A.  Possible values are: ~%~A"
     s name all))
 
 (define (print-file file)
   (call-with-input-file file (function write-all-chars)))
 
 (define (write-all-chars port)
   (let ((line  (read-line port)))
     (if (eof-object? line)
 	'ok
 	(begin
 	  (write-line line)
 	  (write-all-chars port)))))
 
 (define (strings->syms l)
   (map (lambda (x)
 	 (string->symbol (string-upcase x)))
        l))