home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d5xx
/
d556
/
scheme2c.lha
/
Scheme2C
/
Scheme-src.lzh
/
scsc
/
compile.sc
< prev
next >
Wrap
Text File
|
1991-10-11
|
18KB
|
513 lines
;;; The top level of the Scheme compiler is implemented by this module. The
;;; variables that are used outside this module are:
;;;
;* Copyright 1989 Digital Equipment Corporation
;* All Rights Reserved
;*
;* Permission to use, copy, and modify this software and its documentation is
;* hereby granted only under the following terms and conditions. Both the
;* above copyright notice and this permission notice must appear in all copies
;* of the software, derivative works or modified versions, and any portions
;* thereof, and both notices must appear in supporting documentation.
;*
;* Users of this software agree to the terms and conditions set forth herein,
;* and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free
;* right and license under any changes, enhancements or extensions made to the
;* core functions of the software, including but not limited to those affording
;* compatibility with other hardware or software environments, but excluding
;* applications which incorporate this software. Users further agree to use
;* their best efforts to return to Digital any such changes, enhancements or
;* extensions that they make and inform Digital of noteworthy uses of this
;* software. Correspondence should be provided to Digital at:
;*
;* Director of Licensing
;* Western Research Laboratory
;* Digital Equipment Corporation
;* 100 Hamilton Avenue
;* Palo Alto, California 94301
;*
;* This software may be distributed (but not offered for sale or transferred
;* for compensation) to third parties, provided such third parties agree to
;* abide by the terms and conditions of this notice.
;*
;* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
;* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
;* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
;* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
;* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
;* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
;* SOFTWARE.
(module compile)
;;; External and in-line declarations.
(include "plist.sch")
(include "expform.sch")
(include "lambdaexp.sch")
(include "miscexp.sch")
;;; Top-level variables.
(define SC-INPUT '()) ; List of open input files.
(define SC-SPLICE '()) ; List of forms to "splice" into input.
(define SC-SOURCE-NAME '()) ; Initial source file name.
(define SC-INCLUDE-DIRS '("")) ; List of directories for include to search.
(define SC-ICODE '()) ; C written to this file.
(define SC-ERROR '()) ; true -> log errors to ICODE file.
; false -> log errors to STANDARD-OUPUT.
(define SC-ERROR-CNT 0) ; # of error messages reported.
(define SC-LOG '()) ; List of events to log to the SC-ICODE file.
; The possible events are:
;
; SOURCE - source text.
; MACRO - source following macro expansion.
; EXPAND - initial tree.
; CLOSED - closed procedures and variables
; TRANSFORM - tree following boolean transform.
; LAMBDA - lambda analysis information.
; TREE - final tree and constants.
; LAP - lap code.
; PEEP - peep-hole optimization.
(define SC-STACK-TRACE #t) ; true -> emit stack tracing code
; false -> don't emit stack tracing code.
(define SC-INTERPRETER #f) ; true -> building an interpreter, so ignore
; main clause in module.
; false -> process module normally.
(define SC-LOG-DEFAULT '(source macro expand closed transform lambda tree))
; Default list of events to log.
(define MODULE-NAME "") ; Module name.
(define MODULE-NAME-UPCASE "") ; Upper case version of the module name.
(define MAIN-PROGRAM-NAME '()) ; Main program name.
(define HEAP-SIZE 0) ; Default size of heap.
(define CURRENT-DEFINE-NAME 'top-level)
; Name of current DEFINE being processed.
(define TOP-LEVEL-SYMBOLS #t) ; List of top-level symbols.
(define WITH-MODULES '()) ; List of additional modules used.
(define RESTORE-PLIST '()) ; Function to restore default initial values.
(define TRUE-ALPHA 'true-alpha) ; Alpha variable for #t
(define FALSE-ALPHA 'false-alpha)
; Alpha variable for #f
(define EMPTY-LIST-ALPHA 'empty-list-alpha)
; Alpha variable for ()
(define CONS-ALPHA '()) ; Alpha variable for cons
(define UNDEFINED-ALPHA '()) ; Alpha variable for undefined value
;;; Initialization of the entire compiler is triggered by the following
;;; function. It is normally called once at the start of each compilation.
(define (INITIALIZE-COMPILE)
;;; Initialize the variables in expand.sc
(set! lexical-free-vars '())
(set! lexical-bound-vars '())
(set! current-lambda-id 'top-level)
;;; Initialize the variables in miscexp.sc
(set! quote-constants '())
;;; Initialize the variables in lap.sc
(set! lap-code '())
;;; Initialize some of the variables in compile.sc
(set! sc-log '())
(set! sc-stack-trace #t)
(set! sc-interpreter #f)
;;; Initialize the property list.
(if restore-plist
(restore-plist)
(begin (create-plist predef-default)
(copy-plist 'scc 'initial-scc)
(set! restore-plist
(let ((init-globals global-free-vars)
(init-seq make-alpha-seq))
(lambda ()
(set! global-free-vars init-globals)
(set! make-alpha-seq init-seq)
(copy-plist 'initial-scc 'scc))))))
;;; Initialize the rest of the variables in compile.sc
(set! sc-input '())
(set! sc-splice '())
(set! sc-include-dirs '(""))
(set! sc-icode '())
(set! sc-error '())
(set! sc-error-cnt 0)
(set! module-name "")
(set! main-program-name '())
(set! heap-size 0)
(set! current-define-name 'top-level)
(set! top-level-symbols #t)
(set! with-modules '())
(set! undefined-alpha (bound '$_undefined))
(set! cons-alpha (bound 'cons))
(set! module-name ""))
;;; As property list initialization takes a while, it is done only once and a
;;; copy is saved.
(define (CREATE-PLIST predef-file)
(set! module-name "*initialize*")
(copy-plist 'null-property 'scc)
(set! global-free-vars '())
(set! make-alpha-seq 0)
;;; Initialize for #T, #F, and ().
(set-id-vname! 'true-alpha "TRUEVALUE")
(set-id-use! 'true-alpha 'constant)
(set-id-value! 'true-alpha #t)
(set-id-vname! 'false-alpha "FALSEVALUE")
(set-id-use! 'false-alpha 'constant)
(set-id-value! 'false-alpha #f)
(set-id-vname! 'empty-list-alpha "EMPTYLIST")
(set-id-use! 'empty-list-alpha 'constant)
(set-id-value! 'empty-list-alpha '())
;;; Initialize for miscexp.t
(put 'lap 'expand lap-exp )
(put 'quote 'expand quote-exp )
(put 'set! 'expand set!-exp )
(put 'if 'expand if-exp )
(put 'define 'expand define-exp)
;;; Initialize for macros.t
(put 'quasiquote 'macro (old-macro quasiquote-macro))
(put 'cond 'macro (old-macro cond-macro))
(put 'case 'macro (old-macro case-macro))
(put 'and 'macro (old-macro and-macro))
(put 'or 'macro (old-macro or-macro))
(put 'not 'macro (old-macro not-macro))
(put 'begin 'macro (old-macro begin-macro))
(put 'let 'macro (old-macro let-macro))
(put 'let* 'macro (old-macro let*-macro))
(put 'letrec 'macro (old-macro letrec-macro))
(put 'do 'macro (old-macro do-macro))
(put 'when 'macro (old-macro when-macro))
(put 'unless 'macro (old-macro unless-macro))
(put 'quote 'macro quote-macro)
(put 'lap 'macro lap-macro)
(put 'module 'macro quote-macro)
(put 'include 'macro quote-macro)
(put 'define-external 'macro quote-macro)
(put 'define-c-external 'macro quote-macro)
(put 'define 'macro define-macro)
(put 'define-macro 'macro define-macro-macro)
(put 'define-constant 'macro define-constant-macro)
(put 'eval-when 'macro eval-when-macro)
(put 'lambda 'macro lambda-macro)
;;; Initialize for lambdaexp.sc
(put 'lambda 'expand lambda-exp)
;;; Initialize for lap.sc
(load-plist-lap)
;;; Initialize using the predef file.
(set! sc-input (list (open-input-file predef-file)))
(let ((x (read-text)))
(if (not (eof-object? x))
(report-error "Illegal predefinition form:" x)))
(close-port (car sc-input))
(set! sc-input '())
(set! make-alpha-seq (max make-alpha-seq 1000))
;;; Initialize alpha variables which point into the predef file.
(set! undefined-alpha (bound '$_undefined))
(set! cons-alpha (bound 'cons))
(set! module-name ""))
;;; The compiler is invoked by the procedure SC which takes the following
;;; required argument:
;;;
;;; input source file name to compile. The suffix ".sc" is
;;; added to it to form the actual file name.
;;;
;;; and the following optional arguments:
;;;
;;; icode file for C intermediate code. If it is supplied,
;;; then the suffix ".c" will be added to form the file
;;; name. If it is not supplied then it will be
;;; constructed by appending the suffix ".c" to the source
;;; file name.
;;;
;;; ERROR error messages are to be written to the icode file. If
;;; it is not supplied, then errors will be written to the
;;; standard output device.
;;;
;;; LOG log the default events to the icode file. If it is
;;; not specified, then no events will be logged.
;;;
;;; (LOG events...) log the specified events to the icode file. If it is
;;; not specified, then no events will be logged.
;;;
;;; NOTRACE don't emit code for stack back stack. If it is not
;;; specified, then stack trace back code will be emitted.
;;;
;;; PREDEF file source file for predefined functions. If it is
;;; specified, then a suffix of ".sc" will be
;;; appended. If is is not specified, then the "standard"
;;; predefinition file will be used.
(define (SC input . output)
(initialize-compile)
(if (symbol? input) (set! input (string-downcase (symbol->string input))))
(set! sc-source-name (string-append input ".sc"))
(set! sc-input (list (open-input-file sc-source-name)))
(cond ((and output (output-port? (car output)))
(set! sc-icode (car output))
(set! output (cdr output)))
((or (null? output) (pair? (car output))
(memq (car output) '(error log profile predef)))
(set! sc-icode (open-output-file (string-append input ".c"))))
((or (symbol? (car output)) (string? (car output)))
(set! sc-icode
(open-output-file (string-append (if (symbol? (car output))
(string-downcase
(symbol->string (car output)))
(car output)) ".c")))
(set! output (cdr output))))
(do ((output output (cdr output))
(flag '())
(options '() (cons flag options)))
((null? output) (docompile))
(set! flag (car output))
(cond ((memq flag options)
(report-error "Duplicate option:" flag))
((eq? flag 'error)
(set! sc-error #t))
((eq? flag 'log)
(set! sc-log sc-log-default))
((and (pair? flag) (eq? (car flag) 'log))
(set! sc-log (cdr flag))
(set! flag 'log))
((eq? flag 'notrace)
(set! sc-stack-trace #f))
((and (eq? flag 'predef) (cdr output))
(create-plist
(string-append (if (symbol? (cadr output))
(string-downcase
(symbol->string (cadr output)))
(cadr output))
".sc"))
(set! output (cdr output)))
(else (report-error "Unrecognized option:" flag))))
(close-sc-files)
'sc-done)
;;; The following function is called to assure that all the files used by SC
;;; are closed.
(define (CLOSE-SC-FILES)
(let ((cifo (lambda (f)
(if (and f (not (eq? f (current-output-port))))
(close-port f)))))
(for-each cifo sc-input)
(set! sc-input '())
(set! sc-splice '())
(set! sc-include-dirs '(""))
(cifo sc-icode)
(set! sc-icode '())))
;;; SCL is an alternative to SC and is provided for testing. It allows one to
;;; specify a list of expressions to compile. They will be written to the file
;;; "scltext.sc" and then SC will be invoked. The default logging will be
;;; enabled.
(define (SCL . expl)
(let ((file 'scltext))
(cond ((and expl (pair? (car expl)))
(let ((port (open-output-file "scltext.sc")))
(write '(module test) port)
(newline port)
(for-each (lambda (exp) (write exp port) (newline port))
expl)
(close-output-port port)))
(expl
(set! file (car expl))))
(sc file (current-output-port) 'log)))
;;; Event logging is tested for the by the following boolean.
(define (LOG? event) (memq event sc-log))
;;; Once all the files are open, the actual compilation is directed by the
;;; following function.
(define (DOCOMPILE)
(let ((forms '()))
(if sc-log (format sc-icode "/* ***** Expand Forms *****~%"))
(set! forms (expand-forms))
(if (log? 'expand) (pp$t-list forms sc-icode))
(if sc-log (format sc-icode " ***** Transformations *****~%"))
(for-each analyze-closures1a forms)
(for-each analyze-closures1b forms)
(set! forms (map transform forms))
(if sc-log (format sc-icode " ***** Closure Analysis *****~%"))
(for-each analyze-closures2 forms)
(if (log? 'lambda)
(for-each
(lambda (tree)
(walk-$tree
(lambda (l)
(if ($lambda? l)
(begin (print-lambda-info
($lambda-id l)
sc-icode)
(newline sc-icode))))
tree))
forms))
(if (log? 'tree)
(begin (pp$t-list forms sc-icode)
(newline sc-icode)
(pretty-print-$tree quote-constants sc-icode)
(newline sc-icode)))
(if sc-log (format sc-icode " ***** Code Generation ***** */~%"))
(if (zero? sc-error-cnt) (generate-code forms))))
;;; Error messages are written in a standard form to the error file by the
;;; following function. It will also keep a count of the number of errors.
(define (REPORT-ERROR msg . ls)
(if (not sc-error) (set! sc-error (current-output-port)))
(format sc-error "***** ERROR - ~a ~a" current-define-name msg)
(for-each (lambda (l) (format sc-error " ~a" l)) ls)
(newline sc-error)
(set! sc-error-cnt (+ 1 sc-error-cnt)))
;;; Warning messages are written in a standard form to the error file by the
;;; following function.
(define (REPORT-WARNING msg . ls)
(if (not sc-error) (set! sc-error (current-output-port)))
(format sc-error "***** WARNING - ~a ~a" current-define-name msg)
(for-each (lambda (l) (format sc-error " ~a" l)) ls)
(newline sc-error))
;;; $TREE pretty-printer.
(define (PRETTY-PRINT-$TREE tree out)
(let ((indent (write-count out))
(left (- (write-width out) (write-count out))))
(cond ((and ($call? tree) ($lambda? ($call-func tree)))
(let ((lid ($lambda-id ($call-func tree))))
(pretty-print-$tree
`(<apply>
,($call-tail tree)
,lid
,@(pp$t-lambda-bind (lambda-reqvars lid)
(lambda-optvars lid) ($call-argl tree))
,@($lambda-body ($call-func tree)))
out)))
((or (not (pair? tree)) (>= (print-in tree left) 0))
(write tree out))
((and (eq? (car tree) '<apply>)
(>= (print-in (list (car tree) (cadr tree) (caddr tree))
left)
0))
(format out "(~S ~S ~S" (car tree) (cadr tree) (caddr tree))
(for-each
(lambda (x)
(newline out)
(set-write-count! out (+ indent 1))
(pretty-print-$tree x out))
(cdddr tree))
(format out ")"))
((and (memq (car tree) '($define $if $lambda))
(>= (print-in (list (car tree) (cadr tree)) left) 0))
(format out "(~S ~S" (car tree) (cadr tree))
(for-each
(lambda (x)
(newline out)
(set-write-count! out (+ indent 5))
(pretty-print-$tree x out))
(cddr tree))
(format out ")"))
(else
(format out "(")
(pretty-print-$tree (car tree) out)
(let loop ((tree (cdr tree)))
(cond ((pair? tree)
(newline out)
(set-write-count! out (+ indent 2))
(pretty-print-$tree (car tree) out)
(loop (cdr tree)))
(tree
(newline out)
(set-write-count! out (+ indent 2))
(display ". " out)
(pretty-print-$tree tree out))))
(format out ")")))))
(define (PP$T-LAMBDA-BIND reqvars optvars vals)
(cond ((null? reqvars)
(if optvars
`((,(car optvars) <- ,vals))
'()))
(else
(cons `(,(car reqvars) <- ,(car vals))
(pp$t-lambda-bind (cdr reqvars) optvars (cdr vals))))))
(define (PP$T-LIST forms out)
(for-each (lambda (form) (pretty-print-$tree form out) (newline out))
forms))
;;; Space out to a certain column on an output port.
(define (SET-WRITE-COUNT! out cnt)
(do ((i (- cnt (write-count out)) (- i 1)))
((<= i 0))
(write-char #\space out)))
;;; See if an object "s" will print in "len" characters or less. It will
;;; return the number of characters left, or a negative number if the object
;;; won't fit.
(define (PRINT-IN s len)
(if (not (negative? len))
(begin (if (vector? s) (set! s (vector->list s)))
(if (pair? s)
(print-in (cdr s) (- (print-in (car s) len) 1))
(- len (string-length (format "~s" s)))))
len))
;;; Down case a string.
(define (STRING-DOWNCASE s)
(do ((i (- (string-length s) 1) (- i 1))
(t (make-string (string-length s))))
((= i -1) t)
(string-set! t i (char-downcase (string-ref s i)))))
;;; Return the first "n" items of list "l".
(define (LIST-HEAD l n)
(if (zero? n) '() (cons (car l) (list-head (cdr l) (- n 1)))))