home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
s
/
s48.zip
/
MISC
/
COMMAND.SCM
< prev
next >
Wrap
Text File
|
1992-07-06
|
15KB
|
539 lines
; -*- Mode: Scheme; Syntax: Scheme; Package: Scheme; -*-
; Copyright (c) 1992 by Richard Kelsey and Jonathan Rees. See file COPYING.
; This is file command.scm.
; Command processor
(define command-level-type
(make-record-type 'command-level '(cont env condition)))
(define make-command-level
(record-constructor command-level-type '(cont env condition)))
(define command-level-cont (record-accessor command-level-type 'cont))
(define command-level-env (record-accessor command-level-type 'env))
(define command-level-condition
(record-accessor command-level-type 'condition))
; Command loop state:
(define $command-levels (make-fluid '()))
(define $input-port (make-fluid (current-input-port)))
(define $output-port (make-fluid (current-output-port)))
(define $interactive? (make-fluid #f))
(define $command-output (make-fluid 'hello))
(define $package-for-commands $package-for-load)
(define (command-processor resume-arg)
(let-fluid $output-port (current-output-port)
(lambda ()
(let-fluid $input-port (current-input-port)
(lambda ()
(let-fluid $interactive? (not (eq? resume-arg 'batch))
(lambda ()
(command-loop greet-user #f))))))))
; Command loop
(define (command-loop thunk condition)
(internal-catch
(lambda (cont env)
(really-with-handler command-loop-condition-handler
(lambda ()
(let-fluid $command-levels
(cons (make-command-level cont env condition)
(fluid $command-levels))
(lambda ()
(thunk)
(if condition
(display-condition-carefully condition (fluid $output-port)))
(let loop ()
(let ((command (read-command (command-prompt))))
(if (eof-object? command)
(begin (newline)
(pop-command-level))
(execute-command command))
(loop))))))))))
; Command level control: pop, reset, exit
(define (pop-command-level)
(let ((levels (cdr (fluid $command-levels))))
(if (null? levels)
(if (or (not (fluid $interactive?))
(y-or-n? "Exit Scheme48" #t))
(exit 0))
(throw-to-command-level
(car levels)
(lambda ()
(let ((c (command-level-condition (car levels))))
(command-loop (lambda ()
(if c
(display "Back to" (fluid $output-port))))
c)))))))
(define (reset)
(throw-to-command-level (last (fluid $command-levels))
(lambda ()
(command-loop
(lambda ()
(newline (fluid $output-port))
(write-line "Top level" (fluid $output-port)))
#f))))
(define (exit . maybe-status)
(let ((status (if (null? maybe-status)
0
(car maybe-status))))
(throw-to-command-level (last (fluid $command-levels))
(lambda () status))))
(define (throw-to-command-level level thunk)
(internal-throw (command-level-cont level)
(command-level-env level)
thunk))
; Condition handler
(define (command-loop-condition-handler c h)
(cond ((warning? c)
(display-condition-carefully c (fluid $output-port))
(unspecified)) ;proceed
((fluid $interactive?)
(command-loop unspecified c)) ;can proceed via throw-to-command-level
(else ;batch mode
(display-condition-carefully c (fluid $output-port))
(exit 1))))
(define display-condition-carefully
(let ((display display) (newline newline))
(lambda (c port)
(if (error? (ignore-errors (lambda ()
(newline port)
(display-condition c port)
#f)))
(begin (display "(Error in display-condition.)" port)
(newline port))))))
(define (last x)
(if (null? (cdr x))
(car x)
(last (cdr x))))
(define (command-prompt)
(let ((len (- (length (fluid $command-levels)) 1))
(p (fluid $package-for-commands)))
(string-append (if (= len 0)
""
(number->string len))
(if (or (= len 0)
(eq? p user-package))
""
" ")
(if (eq? p user-package)
""
(symbol->string (package-id p)))
"> ")))
(define (greet-user)
(let ((port (fluid $output-port)))
(display "Welcome to Scheme48" port)
(if (fluid $image-info)
(begin (write-char #\space port)
(display (fluid $image-info) port)))
(display "." port)
(newline port)
(write-line "Copyright (c) 1992 by Richard Kelsey and Jonathan Rees." port)
(write-line "Please report bugs to scheme48-bugs@altdorf.ai.mit.edu."
port)
(write-line "Type :? for help." port)))
(define (set-interactive! i)
(set-fluid! $interactive? i))
(define (read-command prompt)
(let ((i-port (fluid $input-port))
(o-port (fluid $output-port)))
(let prompt-loop ()
(if (fluid $interactive?)
(display prompt o-port))
(let loop ()
(let ((c (peek-char i-port)))
(cond ((eof-object? c)
(read-char i-port))
((char-whitespace? c)
(read-char i-port)
(if (char=? c #\newline)
(prompt-loop)
(loop)))
((char=? c #\:)
(read-char i-port)
(read-named-command i-port))
((char=? c #\;)
(gobble-line i-port)
(prompt-loop))
(else
(read-evaluation-command i-port))))))))
(define (read-evaluation-command i-port)
(let ((form (read-form i-port)))
(if (eq? (skip-over horizontal-space? i-port) #\newline)
(read-char i-port))
(lambda ()
(evaluate-and-print form))))
(define (horizontal-space? c)
(and (char-whitespace? c)
(not (char=? c #\newline))))
; Read an S-expression, allowing ## as a way to refer to last command
; output. The use of a procedure circumvents the fact that
; DESYNTAXIFY might copy the value.
(define (read-form port)
(let ((sharp-sharp (fluid $command-output)))
(with-sharp-sharp `(',(lambda () sharp-sharp))
(lambda () (read port)))))
; Commands are implemented as thunks, for now.
(define (execute-command command)
(command))
; Commands
(define (read-named-command port)
(let ((c-name (read port)))
(let ((probe (table-ref command-table c-name)))
(if probe
(read-command-arguments port (car probe) (cdr probe))
(read-command-error port "Unrecognized command name")))))
(define (read-command-arguments port proc descriptions)
(let loop ((args '())
(ds descriptions))
(let ((c (skip-over horizontal-space? port)))
(if (or (eof-object? c)
(char=? c #\newline))
(if (or (null? ds) (eq? (car ds) '&rest))
(begin (read-char port)
(lambda () (apply proc (reverse args))))
(read-command-error port "Too few command arguments"))
(if (null? ds)
(read-command-error port "Too many command arguments")
(if (eq? (car ds) '&rest)
(loop (cons (read-command-argument (cadr ds) port) args)
ds)
(loop (cons (read-command-argument (car ds) port) args)
(cdr ds))))))))
(define (read-command-argument d port)
(case d
((filename)
(read-string port char-whitespace?))
((expression form)
(read-form port))
((name)
(let ((thing (read port)))
(if (symbol? thing) thing (error "invalid name" thing))))
((value)
(eval (read-form port) (fluid $package-for-commands)))
(else (error "invalid argument description" d))))
(define (read-command-error port message)
(write-line message (fluid $output-port))
(read-line port)
(lambda () 'invalid-command))
; Particular commands
(define command-table (make-table))
(define *command-help* '())
(define (define-command name help1 help2 arg-descriptions procedure)
(table-set! command-table name (cons procedure arg-descriptions))
(set! *command-help*
(insert (list (symbol->string name)
(string-append (symbol->string name) " " help1)
help2)
*command-help*
(lambda (z1 z2)
(string<=? (car z1) (car z2))))))
(define (insert x l <)
(cond ((null? l) (list x))
((< x (car l)) (cons x l))
(else (cons (car l) (insert x (cdr l) <)))))
(define (command-help)
(let ((o-port (fluid $output-port))
(widest (reduce max 0 (map (lambda (z) (string-length (cadr z)))
*command-help*))))
(for-each (lambda (s)
(write-line s o-port))
'(
"This is an alpha-test version of Scheme48. You are interacting with"
"the command processor. The command processor accepts either a command"
"or a Scheme form to evaluate. Commands are:"
""))
(for-each (lambda (z)
(display " :" o-port)
(display (pad-right (cadr z) widest #\space) o-port)
(display " " o-port)
(display (caddr z) o-port)
(newline o-port))
*command-help*)
(for-each (lambda (s)
(write-line s o-port))
'(
""
"The expression ## evaluates to the last value displayed by the command"
"processor."
))))
(define-command 'help "" "print this message" '() command-help)
(define-command '? "" "same as :help" '() command-help)
; Evaluate a form
(define (evaluate-and-print form)
(print-command-result (eval form (fluid $package-for-commands))))
(define (print-command-result result)
(if (not (eq? result (unspecified)))
(begin (set-fluid! $command-output result)
(write (value->expression (abbreviate result))
(fluid $output-port))
(newline (fluid $output-port)))))
; Kludgey low-tech alternative to *print-lines*. Used by inspector...
(define $abbreviate-depth (make-fluid #f))
(define (abbreviate thing)
(let ((limit (fluid $abbreviate-depth)))
(if limit
(let abbrev ((thing thing) (depth 1))
(cond ((pair? thing)
(if (> depth limit)
(list abbreviation-marker)
(cons (abbrev (car thing) (+ depth 1))
(abbrev (cdr thing) (+ depth 1)))))
((vector? thing)
(list->vector
(let recur ((i 0) (depth depth))
(if (> depth limit)
(list abbreviation-marker)
(if (>= i (vector-length thing))
'()
(cons (abbrev (vector-ref thing i)
(+ depth 1))
(recur (+ i 1) (+ depth 1))))))))
;; Not good for records with fancy disclose methods
(else thing)))
thing)))
(define abbreviation-marker (string->symbol "..."))
; :load
(define-command 'load "<filename> ..."
"load Scheme source file(s)"
'(&rest filename)
(lambda filenames
(for-each (lambda (filename)
(load filename (fluid $package-for-commands)))
filenames)))
; :build <exp> <filename>
(define (build start filename)
(build-image start filename))
(define-command 'build "<exp> <filename>" "application builder"
'(value filename) build)
;"A heap image written using :dump or :build can be invoked with"
;" s48 -i <filename> [-h <heap size>] [-a <argument>]"
;"For images made with :build <exp> <filename>, <argument> is passed as"
;"a string to the procedure that is the result of <exp>."
(define (build-image start filename)
(write-line (string-append "Writing " filename) (fluid $output-port))
(let ((state (get-dynamic-state)))
(call-with-current-continuation
(lambda (cc)
(flush-the-symbol-table!)
(write-image filename
(usual-resumer start)
(lambda ()
(initialize!) ;write-image opcode does (clear-registers)
(set-dynamic-state! state)
(cc #t)))))))
; :dump <filename>
(define (dump filename)
(let ((info (fluid $next-image-info)))
(set-fluid! $next-image-info "(suspended image)")
(build-image (lambda (arg)
(restore-the-symbol-table!)
(let-fluid $image-info info
(lambda ()
(command-processor arg))))
filename)))
(define-command 'dump "<filename>" "write a heap image"
'(filename) dump)
(define $image-info (make-fluid #f))
(define $next-image-info (make-fluid #f))
; Set image info for next :dump command.
(define-command 'identify-image "<exp>" "set identification for :dump"
'(expression)
(lambda (string)
(set-fluid! $next-image-info string)))
; :reset
(define-command 'reset "" "top level"
'() reset)
; exit
(define-command 'exit "" "leave" '(&rest value) exit)
; batch
(define (batch)
(let ((i (not (fluid $interactive?))))
(set-interactive! i)
(write-line (if i
"Interactive mode"
"Batch mode")
(fluid $output-port))))
(define-command 'batch "" "toggle batch mode (no prompt, errors exit)"
'() batch)
; :enable, :disable
(define (enable)
(set-fluid! $package-for-commands system-package))
(define-command 'enable "" "system internal environment"
'() enable)
(define (disable)
(set-fluid! $package-for-commands user-package))
(define-command 'disable "" "user environment"
'() disable)
; User package
(define user-package
(make-package 'user (list scheme-package)))
(environment-define! user-package
'load
(lambda (filename)
(load filename user-package)))
; Utilities for command processor
(define (command-loop-continuation) ;utility by debugger
(command-level-cont (car (fluid $command-levels))))
(define (y-or-n? question eof-value)
(let ((i-port (fluid $input-port))
(o-port (fluid $output-port)))
(let loop ((count *y-or-n-eof-count*))
(display question o-port)
(display " (y/n)? " o-port)
(let ((line (read-line i-port)))
(cond ((eof-object? line)
(newline)
(if (= count 0)
eof-value
(begin (display "I'll only ask another " o-port)
(write count o-port)
(display " times.")
(newline)
(loop (- count 1)))))
((< (string-length line) 1) (loop count))
((char=? (string-ref line 0) #\y) #t)
((char=? (string-ref line 0) #\n) #f)
(else (loop count)))))))
(define *y-or-n-eof-count* 100)
(define (pad-right string width padchar)
(let ((n (- width (string-length string))))
(if (<= n 0)
string
(string-append string (make-string n padchar)))))
(define (write-line string port)
(write-string string port)
(newline port))
(define (read-line port)
(let loop ((l '()) (n 0))
(let ((c (read-char port)))
(if (eof-object? c)
c
(if (char=? c #\newline)
(reverse-list->string l n)
(loop (cons c l) (+ n 1)))))))
(define (read-string port delimiter?)
(let loop ((l '()) (n 0))
(let ((c (peek-char port)))
(cond ((or (eof-object? c)
(delimiter? c))
(reverse-list->string l n))
(else
(loop (cons (read-char port) l) (+ n 1)))))))
(define (skip-over pred port)
(let ((c (peek-char port)))
(cond ((eof-object? c) c)
((pred c) (read-char port) (skip-over pred port))
(else c))))
; Find space-separated fields
;
;(define (parse-resume-argument string)
; (define limit (string-length string))
; (define (find pred i)
; (cond ((>= i limit) #f)
; ((pred (string-ref string i)) i)
; (else (find pred (+ i 1)))))
; (define (not-char-whitespace? c)
; (not (char-whitespace? c)))
; (if (= limit 0)
; '()
; (let recur ((i 0))
; (let ((i (find not-char-whitespace? i)))
; (if i
; (let ((j (find char-whitespace? i)))
; (if j
; (cons (substring string i j)
; (recur j))
; (list (substring string i limit))))
; '())))))
; (put 'with-sharp-sharp 'scheme-indent-hook 1)
; (put 'internal-catch 'scheme-indent-hook 0)