; -*- Mode: Lisp -*- Filename: pcomp.s ;--------------------------------------------------------------------------; ; ; ; TI SCHEME -- PCS Compiler ; ; Copyright 1985 (c) Texas Instruments ; ; ; ; Terry Caudill ; ; ; ; Compiler Specific runtime routines ; ; ; ;--------------------------------------------------------------------------; ; Revision history: ; 6/01/87 tc - This file was created from several of the old compiler ; files (pstd, pio, pdebug, pchreq, and pstl) in order to ; collect all the compiler-specific code. ; 6/01/87 tc - added PCS-INTEGRATE-DEFINE variable so that MIT style ; defines don't expand into named-lambda unless #T. This ; is a requirement for the R^3 Report. ; 6/01/87 tc - added string->number as autoload from PNUM2S ; 6/01/87 tc - make compiler re-entrant ; 6/01/87 rb - added more PGR functions to autoload; ; toplevel reworked so RESET doesn't affect the fluids ; INPUT-PORT and OUTPUT-PORT (this allows the system toplevel ; to run in windows other than 'CONSOLE); ; revamped PCS-INITIAL-ARGUMENTS per 3.0 changes to cmd line ; 6/01/87 tc - added MAKE-STRING as autoload for PFUNARG ;;; ;;; The following functions are related in that they all envoke the ;;; compiler in some form or fashion ;;; (define load ; LOAD (lambda (filename) (let ((i-port (open-input-file filename))) (if (null? i-port) (error "Unable to load file" filename) (letrec ((loop (lambda (form) (cond ((eof-object? form) (close-input-port i-port) 'ok) (else (eval form) (loop (read i-port))))))) (let ((form (read i-port))) (if (eq? form '#!fast-load) (begin (close-input-port i-port) (fast-load filename)) (loop form)))))))) (define compile-file ; COMPILE-FILE (lambda (filename1 filename2) (if (or (not (string? filename1)) (not (string? filename2)) (equal? filename1 filename2)) (%error-invalid-operand-list 'COMPILE-FILE filename1 filename2) (let ((i-port (open-input-file filename1))) (let ((o-port (open-output-file filename2))) (set-line-length! 74 o-port) (letrec ((loop (lambda (form) (if (eof-object? form) (begin (close-input-port i-port) (close-output-port o-port) 'ok) (begin ; no COMPILE-FORMS (compile-to-file form) (set! form '()) ; for GC (loop (read i-port)))))) (compile-to-file (lambda (form) (let ((cform (compile form))) (write (list '%execute (list 'quote cform)) o-port) (newline o-port) (%execute cform))))) (loop (read i-port)))))))) (define %compile-timings '()) (define %compile ; %COMPILE (lambda (exp . time?) (when time? (gc)) (let ((time '()) (t0 (runtime))) (set! pcs-local-var-count 0) (set! pcs-error-flag #!false) (set! pcs-verbose-flag (not time?)) (set! pcs-binary-output #!false) (set! pme= (pcs-macro-expand exp)) (if pcs-error-flag (error "[Compilation terminated because of errors]") (begin (set! time (cons (- (runtime) t0) time)) (set! psimp= (pcs-simplify pme=)) (set! time (cons (- (runtime) t0) time)) (pcs-closure-analysis psimp=) (set! time (cons (- (runtime) t0) time)) (set! pcg= (pcs-gencode psimp=)) (set! time (cons (- (runtime) t0) time)) (set! ppeep= (pcs-postgen pcg=)) (set! time (cons (- (runtime) t0) time)) (set! pasm= (pcs-assembler ppeep=)) (set! time (cons (- (runtime) t0) time)) (set! pcs-verbose-flag #!false) (when time? (set! %compile-timings (cons (reverse! time) %compile-timings))) pasm=))))) ; ; Make compiler re-entrant (or more so, at any rate). The problem arises ; when a macro evokes EVAL and thus COMPILE during macro expansion i9n PME ; (define compile '()) ; COMPILE (let ((ge (%set-global-environment user-global-environment))) (set! compile (lambda (exp) (let* ((vc pcs-local-var-count) ; save (vf pcs-verbose-flag) (ef pcs-error-flag) (bo pcs-binary-output) (gensym-string (access string (procedure-environment gensym))) (gensym-counter (access counter (procedure-environment gensym))) (result (pcs-assembler (pcs-compile-to-AL exp)))) (set! pcs-local-var-count vc) ; restore (set! pcs-verbose-flag vf) (set! pcs-error-flag ef) (set! pcs-binary-output bo) (set! (access string (procedure-environment gensym)) gensym-string) (set! (access counter (procedure-environment gensym)) gensym-counter) (pcs-clear-registers) result))) (%set-global-environment ge)) (define pcs-compile-to-AL ; PCS-COMPILE-TO-AL (lambda (exp) (set! pcs-local-var-count 0) (set! pcs-error-flag #!false) (set! pcs-binary-output #!true) (set! pcs-verbose-flag #!false) (let ((t1 (pcs-macro-expand exp))) (if pcs-error-flag (error "[Compilation terminated because of errors]") (begin (set! exp '()) ; for GC (pcs-clear-registers) (let ((t2 (pcs-simplify t1))) (pcs-closure-analysis t2) (let ((t3 (pcs-gencode t2))) (set! t2 '()) ; for GC (pcs-clear-registers) (let ((t4 (pcs-postgen t3))) (pcs-clear-registers) t4)))))))) (define pcs-execute-AL ; PCS-EXECUTE-AL (lambda (al) (let ((t1 (pcs-assembler al))) (pcs-clear-registers) (%execute t1)))) (define optimize! ; OPTIMIZE! (lambda args (let ((flag (or (null? args)(car args)))) (set! pcs-permit-peep-1 flag) (set! pcs-permit-peep-2 flag)))) ;;;; Syntax Checking Functions ;;; ;;; These functions may be used by macros and other syntax transformers ;;; to help find violations of Scheme syntax rules. Note that these ;;; check only the syntax, not semantics, of the program fragments they ;;; are defined for. It is the caller's responsibility, for example, to ;;; verify that all of the identifiers bound in a LETREC are distinct. ;;; PCS-CHK-PAIRS can't do so, because it is called to verify pairs for ;;; both LETREC and LET*. (define pcs-chk-id ; PCS-CHK-ID (lambda (e y) (when (not (symbol? y)) (syntax-error "Invalid identifier in expression" y e)))) (define (pcs-chk-length= e y n) ; PCS-CHK-LENGTH= (cond ((and (null? y)(zero? n)) '()) ((null? y) (syntax-error "Expression has too few subexpressions" e)) ((atom? y) (syntax-error (if (atom? e) "List expected" "Expression ends with `dotted' atom") e)) ((zero? n) (syntax-error "Expression has too many subexpressions" e)) (else (pcs-chk-length= e (cdr y) (sub1 n))))) (define (pcs-chk-length>= e y n) ; PCS-CHK-LENGTH>= (cond ((and (null? y)( < n 1)) '()) ((atom? y) (pcs-chk-length= e y -1)) (else (pcs-chk-length>= e (cdr y) (sub1 n))))) (define (pcs-chk-bvl e bvl dot-ok?) ; PCS-CHK-BVL (letrec ((oops (lambda () (syntax-error "Invalid identifier list" e)))) (cond ((atom? bvl) (or (null? bvl)(and dot-ok? (pcs-chk-bvar bvl)) (oops))) ((pcs-chk-bvar (car bvl)) (pcs-chk-bvl e (cdr bvl) dot-ok?)) (else (oops))))) (define (pcs-chk-pairs e pairs) ; PCS-CHK-PAIRS (letrec ((oops (lambda () (syntax-error "Invalid pair binding list" e)))) (if (atom? pairs) (or (null? pairs) (oops)) (let ((pr (car pairs))) (if (or (atom? pr) (not (pcs-chk-bvar (car pr))) (atom? (cdr pr)) (not (null? (cddr pr)))) (oops) (pcs-chk-pairs e (cdr pairs))))))) (define pcs-chk-bvar ; PCS-CHK-BVAR (lambda (id) (if (or (not (symbol? id)) (getprop id 'PCS*MACRO) (memq id '(QUOTE LAMBDA IF SET! BEGIN LETREC DEFINE)) (and (memq id '(T NIL)) pcs-integrate-t-and-nil)) (syntax-error "Invalid bound variable name" id) #!true))) ;;; EXPAND, EXPAND-MACRO and EXPAND-MACRO-1 expand macro calls. EXPAND-MACRO ;;; and EXPAND-MACRO-1 only expand the outer-level form and leave sub-forms ;;; alone. EXPAND-MACRO-1 does so only once, while EXPAND-MACRO does so ;;; repeatedly until there is no change. EXPAND expands form and all subforms ;;; completely. (define expand-macro ; EXPAND-MACRO (lambda (exp) (let ((expansion (expand-macro-1 exp))) (if (or (atom? exp) (equal? expansion exp)) expansion (expand-macro expansion))))) (define expand-macro-1 ; EXPAND-MACRO-1 (lambda (x) (cond ((symbol? x) (let ((entry (getprop x 'PCS*MACRO))) (if (null? entry) x (if (pair? entry) (if (eq? (car entry) 'ALIAS) (cdr entry)) (syntax-error "Macro or special form name used as a variable" x))))) ((pair? x) (let* ((f (car x)) (ef (if (pair? f) (expand-macro f) f)) (a (cdr x))) (if (symbol? ef) (let ((macfun (getprop ef 'PCS*MACRO))) (cond ((null? macfun) (cons ef a)) ((pair? macfun) (cons (cdr macfun) a)) (else (macfun (cons ef a))))) (cons ef a)))) (else x)))) (define expand ; EXPAND (letrec ((expand-item (lambda (item) (if (pair? item) (expand item) item)))) (lambda (exp) (let ((expansion (expand-macro exp))) (map expand-item expansion))))) ;;; ;;; Set up EDWIN so that it may be loaded into its own environment ;;; (define initiate-edwin ; INITIATE-EDWIN (lambda () (unbind 'edwin user-global-environment) (set! (access edwin-environment user-global-environment) (make-hashed-environment)) (%reify! edwin-environment 0 user-initial-environment) (autoload-from-file (%system-file-name "edwin0.fsl") '(edwin) edwin-environment) (edwin))) (define edwin initiate-edwin) ; EDWIN ;;; ;;; Set up compiler-related global variables ;;; (BEGIN (define %pcs-stl-debug-flag #!false) (define %pcs-stl-history '(%PCS-STL-HISTORY)) ; getprop tag (define pcs-local-var-count 0) (define pcs-integrate-integrables #!true) (define pcs-integrate-primitives #!true) (define pcs-integrate-T-and-NIL #!true) (define pcs-integrate-define #!true) (define pcs-debug-mode #!false) ; debug mode OFF (define pcs-permit-peep-1 #!true) ; optimization ON (define pcs-permit-peep-2 #!true) (define pcs-verbose-flag #!false) (define pcs-display-warnings #!true) (define pme= '()) (define psimp= '()) (define pcg= '()) (define ppeep= '()) (define pasm= '()) ) ;;; Evaluation ;;; EVAL is part interpreter, but calls the compiler for complicated ;;; expressions. In particular, it does not do any bindings ;;; interpretively, since they would have to be first-class ;;; environments and the compiler might be able to do better. (define eval (letrec ((eval-exp (lambda (xx env) (let ((x (expand-macro xx))) (if (pair? x) (case (car x) ((QUOTE) (eval-quote x env)) ((IF) (eval-if x env)) ((SET!) (eval-set! x env)) ((DEFINE) (eval-define x env)) ((BEGIN) (eval-begin x env)) ((LET LET* LETREC LAMBDA ) (eval-compile x env)) ((%%GET-FLUID%%) (eval-fluid x env)) ((%%SET-FLUID%%) (eval-set-fluid! x env)) ((THE-ENVIRONMENT) env) ((PCS-CODE-BLOCK) (eval-execute x env)) (else (eval-application x env))) (eval-atom x env))))) (lookup-binding ; LOOKUP-BINDING (lambda (sym) ; The following is the object code to lookup/fetch ; the binding of sym. It must be passed to %execute with ; the desired environment. (list 'pcs-code-block 1 4 (list sym) '( 7 4 0 ; Ld-global r1,sym 59)))) ; exit (eval-atom ; EVAL-ATOM (lambda (x env) (cond ((not (symbol? x)) x) ((memq x '(#!TRUE #!FALSE #!UNASSIGNED)) x) (else (let ((entry (and PCS-INTEGRATE-T-AND-NIL (assq x '((T #T) (NIL #F)))))) (if entry (cadr entry) ;else (or (lookup-integrable x env) (eval-execute (lookup-binding x) env)))))))) (lookup-integrable (lambda (x env) (let ((info (getprop x 'PCS*PRIMOP-HANDLER))) (and info (pair? info) (eval-exp (cdr info) env))))) (eval-quote ; EVAL-QUOTE (lambda (x env) (pcs-chk-length= x x 2) (cadr x))) (eval-id-error (lambda (err caller env) (syntax-error (string-append "Invalid identifier for " caller ": ") err))) (eval-if ; EVAL-IF (lambda (x env) (if (or (atom? (cdr x)) ; No Pred (atom? (cddr x)) ; No Then (pair? (cdddr x))) ; has ELSE (pcs-chk-length= x x 4) (pcs-chk-length= x x 3)) (cond ((eval-exp (cadr x) env) (eval-exp (caddr x) env)) ((pair? (cdddr x)) (eval-exp (cadddr x) env)) (else #!FALSE)))) (set-var-value ; SET-VAR-VALUE (lambda (sym value) ; The following is the object code code to set the value ; of a variable. It must be passed to %execute with the ; desired environment. (list 'pcs-code-block 2 7 (list sym value) '( 1 4 1 ; Load r1, value 15 4 0 ; St-glob-env r1,sym 59)))) ; exit (eval-set! ; EVAL-SET! (lambda (x env) (pcs-chk-length= x x 3) (let* ((id (cadr x)) (var (expand-macro id)) (value (eval-exp (caddr x) env))) (cond ((not (pair? var)) (cond ((or (not (symbol? var)) (not (eq? var (expand-macro var)))) (eval-id-error var "SET!" env)) ((getprop var 'PCS*PRIMOP-HANDLER) ; this is for primitives and define-integrables (eval-compile x env)) (else (eval-execute (SET-VAR-VALUE var value) env)))) (else (eval-id-error var "SET!" env)))))) (def-var ; DEF-VAR (lambda (sym value) ; The following is the object code code to define a variable ; in a given environment. It must be passed to %execute with the ; desired environment. (list 'pcs-code-block 2 7 (list sym value) '( 1 4 1 ; Load r1, value 31 4 0 ; define! value,sym 59)))) ; exit (eval-define ; EVAL-DEFINE (lambda (x env) (pcs-chk-length>= x x 3) (if (and (pair? (caddr x)) (memq (caaddr x) '(LAMBDA NAMED-LAMBDA))) (eval-compile x env) ;else (let* ((id (cadr x)) (var (expand-macro id)) (value (eval-exp (caddr x) env))) (cond ((not (pair? var)) (cond ((or (not (symbol? var)) (not (eq? var (expand-macro var)))) (eval-id-error var "DEFINE" env)) ((getprop var 'PCS*PRIMOP-HANDLER) ; this is for primitives and define-integrables (eval-compile x env)) (else (eval-execute (DEF-VAR var value) env) id))) (else (eval-id-error var "DEFINE" env))))))) (eval-begin ; EVAL-BEGIN (lambda (x env) (pcs-chk-length>= x x 1) (let loop ((x (cdr x))) (if (null? (cdr x)) (eval-exp (car x) env) (begin (eval-exp (car x) env) (loop (cdr x))))))) (lookup-fluid ; LOOKUP-FLUID (lambda (sym) ; The following is the object code to lookup/fetch the ; fluid binding of sym. It must be passed to %execute with ; the desired environment. (list 'pcs-code-block 1 4 (list sym) '( 8 4 0 ; Ld_fl r1,sym 59)))) ; exit (eval-fluid ; EVAL-FLUID (lambda (x env) (pcs-chk-length= x x 2) (eval-execute (lookup-fluid (eval-exp (cadr x) env)) env))) (set-fluid-var ; SET-FLUID-VAR (lambda (sym value) ; The following is the object code to set the value of a ; fluid variable. It must be passed to %execute with the ; desired environment. (list 'pcs-code-block 2 7 (list sym value) '( 1 4 1 ; Load r1, value 16 4 0 ; St-fl r1,sym 59)))) ; exit (eval-set-fluid! ; EVAL-SET-FLUID! (lambda (x env) (pcs-chk-length>= x x 2) (let ((sym (eval-exp (cadr x) env)) (val (eval-exp (caddr x) env))) (pcs-chk-id x sym) (eval-execute (set-fluid-var sym val) env)))) (eval-application ; EVAL-APPLICATION (lambda (x env) (pcs-chk-length>= x x 1) (let ((proc (eval-exp (car x) env))) (when (not (or (procedure? proc) (and (pair? proc) (eq? (car proc) 'LAMBDA)))) (error-procedure "Attempt to call a non-procedural object" (cons proc (cdr x)) env)) (let ((args (eval-args (cdr x) env))) (let* ((saved-env (%set-global-environment env)) (result (apply proc args))) (%set-global-environment saved-env) result))))) (eval-args ; EVAL-ARGS (lambda (x env) (if (null? x) '() (cons (eval-exp (car x) env) (eval-args (cdr x) env))))) (eval-compile ; EVAL-COMPILE (lambda (x env) (eval-execute (compile x) env))) (eval-execute ; EVAL-EXECUTE (lambda (x env) (let* ((saved-env (%set-global-environment env)) (result (%execute x))) (%set-global-environment saved-env) result))) ) ; letrec vars (lambda (exp . rest) (let* ((env (cond ((null? rest) (let ((e (%set-global-environment user-initial-environment))) (%set-global-environment e) e)) ((not (environment? (car rest))) (%error-invalid-operand 'EVAL (car rest))) (else (car rest)))) (result (eval-exp exp env))) result))))