;;; -*- Mode: Lisp; Syntax: Common-lisp; Package: (PSEUDOSCHEME :USE (LISP) :SHADOW (+ - * / ASSOC CASE ERROR EVAL LAMBDA LET LET* LOOP MAKE-STRING MAP MEMBER NIL PEEK-CHAR PRINT READ READ-CHAR T WRITE)) -*- ;;; Written by Jonathan Rees, summer & fall 1986 ;;; with help from Eric Benson, Kent Pitman, and John Ramsdell. ;;; There is a separate docuemntation file, please read it. (in-package 'pseudoscheme :use '(lisp)) (shadow '( + - * / assoc case error eval lambda let let* loop ;Symbolics Rel 7 loses make-string map member nil peek-char print read read-char t write )) (export '( declare-pseudoscheme-package in-package use-package export import provide require shadow trace untrace compile-file convert-arglist describe ed scheme )) (eval-when (lisp:eval load compile) (defconstant nil lisp:nil) (defconstant t lisp:t) (defconstant else lisp:t) (defparameter *preprocess?* nil) ;set to t after bootstrap is complete ) (eval-when (lisp:eval load compile) (defun sharp-T-read-macro (stream subchar arg) #-Symbolics (declare (ignore stream subchar arg)) #+Symbolics (progn stream subchar arg) t) (defun sharp-F-read-macro (stream subchar arg) #-Symbolics (declare (ignore stream subchar arg)) #+Symbolics (progn stream subchar arg) nil) (defvar cl-sharp-sharp #+Symbolics #'(lisp:lambda (&rest ignore) (error "Symbolics loses")) #-Symbolics (get-dispatch-macro-character #\# #\#)) (defun sharp-sharp-read-macro (stream subchar arg) (cond (arg (funcall cl-sharp-sharp stream subchar arg)) (t 'lisp:*))) (set-dispatch-macro-character #\# #\# #'sharp-sharp-read-macro) (set-dispatch-macro-character #\# #\T #'sharp-T-read-macro) (set-dispatch-macro-character #\# #\F #'sharp-F-read-macro) ;;; Generally useful utility: (defun concatenate-symbol (&rest things) (values (intern (apply #'concatenate 'string (mapcar #'(lisp:lambda (thing) (if (numberp thing) (with-output-to-string (s) (princ thing s)) (string thing))) things))))) (defparameter concatenate-symbol #'concatenate-symbol) ;;; Implementing Scheme global variables well is not at all easy. #| For each global variable, we keep track of a "reference history" and its current "definition method". The reference history is some subset of the following keywords: :variable x :setq (setq x ...) :set!-globally (set!-globally x ...) :function (x ...) at a point when definition status was :function :macro (x ...) at a point when definition status was :macro The definition method is one of: :variable (setq x ...) / (defun x (&rest args) (apply x args)) :constant (defconstant x ...) :function (defun x ...) / (setq x #'x) :macro (defmacro x ...) / (setq x #'x[closed]) Here's how we use this information: 1. When we see a reference to variable x, we may generate either x or #'x. Correctness considerations: -- x is always correct. -- #'x is correct iff definition status isn't :macro. Efficiency considerations: -- x is always preferable to #'x. 2. When we see a call (x ...), we may generate either (x ...) or (funcall x ...). Correctness considerations: -- (funcall x ...) is always correct. -- (x ...) is correct iff method isn't :constant. Efficiency considerations: -- (funcall x ...) is preferable if status is :variable. -- (x ...) is preferable otherwise. 3. When we see an assignment (set! x ...), we may generate either (setq x ...) or (set!-globally x ...). Correctness considerations: -- We are quite screwed if status is :macro. -- setq is correct only if status is (and/or will be) :variable. -- set!-globally is always correct. Efficiency considerations: -- Want (setq x ...) if at all possible. 4. When we see (define x ...), we can generate any of the variants enumerated above. Correctness considerations: -- :variable is always correct, -- :function is correct iff [rhs is lambda and] :setq isn't in history, -- :macro is correct iff [rhs is lambda and] :function and :setq aren't in history. Efficiency considerations: -- :variable is never preferable (unless :setq is in history, in which case it's forced). -- :macro is always preferable when it's correct and the function body is small. It is possible to get screwed by this mechanism, if you compile a file under one set of assumptions and then load it where an incompatible set is in effect. |# (defun use-method (var method) (cond ((and (eq method :function) (lisp:member :setq (get var 'history))) (cerror "proceed as if everything was all right" "(DEFINE ~S ...) is being implemented as (DEFUN ~S ...), but~ ~%some previously generated code assumed that it was OK to~ ~%implement (SET! ~S ...) as (SETQ ~S ...)." var var var var)) ((and (eq method :macro) (lisp:member :function (get var 'history))) (cerror "proceed as if everything was all right" "(DEFINE ~S ...) is being implemented as (DEFMACRO ~S ...), but~ ~%some code containing a call (~S ...) was compiled before this ~%definition was seen." var var var))) (setf (get var 'method) method)) (defun record-variable-usage (var usage) (lisp:let ((h (get var 'history))) (if (not (lisp:member usage h)) (setf (get var 'history) (cons usage h))))) (use-method 't :variable) (use-method 'nil :variable) (defun lexical-name (var) (or (get var 'lexical-name) ;Speed hacque (lisp:let ((*package* (symbol-package var))) (lisp:let ((lex (concatenate-symbol "." var))) (setf (get var 'lexical-name) lex) lex)))) );nehw-lave ;;; Scheme names database (eval-when (lisp:eval load compile) (defmacro entry-type (entry) `(car ,entry)) (defmacro entry-name (entry) `(cadr ,entry)) (defmacro entry-dests (entry) `(caddr ,entry)) (defmacro entry-provision (entry) `(cadddr ,entry)) (defun inherit? (entry) (and (eq (entry-type entry) 'proc) (lisp:member (entry-provision entry) '(cl ~cl)))) (defun renaming? (entry) (lisp:let ((prov (entry-provision entry))) (and (eq (entry-type entry) 'proc) (consp prov) (lisp:member (car prov) '(=cl ~cl))))) );nehw-lave (defun set-up-database-entry (entry) (lisp:let ((name (entry-name entry))) (setf (get name 'scheme-info) entry) (cond ((inherit? entry) ;; Inheriting from CL -- just set the value cell. (if (and (boundp name) (not (eq (symbol-value name) (symbol-function name)))) (cerror "clobber the value that's already there" "package pollution! -- ~s is bound" name)) (use-method name :function)) ((renaming? entry) (lisp:let ((src (cadr (entry-provision entry)))) (if (and (boundp name) (not (eq (symbol-value name) (symbol-function src)))) (cerror "clobber value that's already there" "package pollution! -- ~s is bound" name)) (if (and (fboundp name) (not (macro-function name))) (cerror "clobber function that's already there" "package pollution! -- ~s is defined" name)) (use-method name :macro)))) (entry-name entry))) ;;; DEFMACRO is the most reliable way to get efficient compilation. ;;; PROCLAIM INLINE may lose. (defmacro define-scheme-database (&body db) `(progn 'compile (defparameter scheme-names (mapcar #'set-up-database-entry ',db)) ;; Work around @#%$$% 3600 bug. ,@(lisp:let ((names (mapcar #'(lisp:lambda (entry) (entry-name entry)) db))) (if (< (length names) #+Symbolics 200 #-Symbolics 1000000) `((export ',names)) `((export ',(subseq names 0 200)) (export ',(subseq names 200))))) ,@(mapcan #'(lisp:lambda (entry) (lisp:let ((name (entry-name entry))) (cond ((inherit? entry) `((defparameter ,name #',name))) ((renaming? entry) (lisp:let ((src (cadr (entry-provision entry)))) `((defmacro ,name (&rest x) `(,',src ,@x)) (defparameter ,name #',src)))) (t '())))) db))) ;;; Entries within each major category are in alphabetical order. ;;; "ps" stands for "pseudoscheme". ;;; "DESTS" is a list of the dialects to which this feature belongs: ;;; E = essential subset of Revised^3 Scheme ;;; R = non-essential Revised^3 Scheme ;;; T = Yale Scheme ;;; M = MIT Scheme ;;; S = used in Abelson & Sussman (define-scheme-database ;;(TYPE NAME (DESTS) PROVIDED-BY) (random else (E T S) ps) (binding nil (R T S) ps) (binding t (R T S) ps) (binding the-empty-stream ( S) ps) (binding user-initial-environment ( S) ps) (syntax and (R T ) CL) (syntax begin (E ) ps) (syntax case (R T ) ps) (syntax cond (E T S) CL) (syntax cons-stream ( S) ps) (syntax define (E T S) ps) (syntax define-macro ( T ) ps) (syntax delay (R T S) ps) (syntax do (R T ) CL) (syntax if (E T S) CL) (syntax lambda (E T S) ps) (syntax let (E ) ps) (syntax let* (R T ) ps) (syntax letrec (E ) ps) (syntax or (R T ) CL) (syntax quasiquote (R ) ps) (syntax quote (E T S) CL) (syntax sequence ( S) (= begin)) (syntax set! (E ) ps) (syntax unquote ( ) ps) (syntax unquote-splicing ( ) ps) (proc * (R T ) (=CL lisp:*)) (proc + (R T ) (=CL lisp:+)) (proc -1+ ( T S) (=CL 1-)) (proc - (R T ) (=CL lisp:-)) (proc / (E T S) (=CL lisp:/)) (proc 1+ ( T S) CL) (proc <= (E T S) CL) (proc < (E T S) CL) (proc = (E T S) CL) (proc >= (E T S) CL) (proc > (E T S) CL) (proc abs (E T S) CL) (proc acos (R T ) CL) (proc angle (R ) (=CL phase)) (proc append (E T ) CL) (proc apply (E T ) ~CL) (proc asin (R T ) CL) (proc assoc (E S) ps) (proc assq (E T S) ps) (proc assv (E ) (=CL lisp:assoc)) (proc atan (R ) CL) (proc atom? ( T S) (=CL atom)) ;Yuck (proc boolean? (E T ) ps) (proc caaaar (E T S) CL) (proc caaadr (E T S) CL) (proc caaar (E T S) CL) (proc caadar (E T S) CL) (proc caaddr (E T S) CL) (proc caadr (E T S) CL) (proc caar (E T S) CL) (proc cadaar (E T S) CL) (proc cadadr (E T S) CL) (proc cadar (E T S) CL) (proc caddar (E T S) CL) (proc cadddr (E T S) CL) (proc caddr (E T S) CL) (proc cadr (E T S) CL) (proc call-with-current-continuation (E ) ps) (proc call-with-input-file (E ) ps) (proc call-with-output-file (E ) ps) (proc car (E T S) CL) (proc cdaaar (E T S) CL) (proc cdaadr (E T S) CL) (proc cdaar (E T S) CL) (proc cdadar (E T S) CL) (proc cdaddr (E T S) CL) (proc cdadr (E T S) CL) (proc cdar (E T S) CL) (proc cddaar (E T S) CL) (proc cddadr (E T S) CL) (proc cddar (E T S) CL) (proc cdddar (E T S) CL) (proc cddddr (E T S) CL) (proc cdddr (E T S) CL) (proc cddr (E T S) CL) (proc cdr (E T S) CL) (proc ceiling (R ) ~CL) (proc char->integer (E ) (=CL char-code)) (proc char-alphabetic? (R ) (=CL alpha-char-p)) (proc char-ci<=? (R ) (=CL char-not-greaterp)) (proc char-ci=? (R ) (=CL char-not-lessp)) (proc char-ci>? (R ) (=CL char-greaterp)) (proc char-downcase (R ) CL) (proc char-lower-case? (R ) (=CL lower-case-p)) (proc char-numeric? (R ) (=CL digit-char-p)) (proc char-ready? (R ) ?) (proc char-upcase (R ) CL) (proc char-upper-case? (R ) (=CL upper-case-p)) (proc char-whitespace? (R ) ps) (proc char<=? (E ) (=CL char<=)) (proc char=? (E ) (=CL char>=)) (proc char>? (E ) (=CL char>)) (proc char? (E T ) (=CL characterp)) (proc close-input-port (R ) (=CL close)) (proc close-output-port (R ) (=CL close)) (proc complex? (E ) (=CL numberp)) (proc cons (E T S) CL) (proc cons* ( T ) (=CL list*)) ;For quasiquote (proc cos (R T S) CL) (proc current-input-port (E ) ps) (proc current-output-port (E ) ps) (proc denominator (R ) CL) (proc display (R T ) (~CL princ)) (proc empty-stream? ( S) (=CL null)) (proc eof-object? (E ) ps) (proc eq? (E T S) (=CL eq)) (proc equal? (E ) (=CL equal)) (proc eqv? (E ) (=CL eql)) (proc error ( T S) ps) (proc eval ( T S) ps) (proc even? (E T ) (=CL evenp)) (proc exact? (E ) ps) (proc exact->inexact (R ) -) (proc expt (R T S) CL) (proc exp (R T S) CL) (proc floor (R ) ~CL) (proc for-each (R ) (~CL mapc)) (proc force (R S) ps) (proc gcd (R S) CL) (proc head ( S) (=CL car)) (proc imag-part (R ) (=CL imagpart)) (proc inexact? (E ) ps) (proc inexact->exact (R ) -) (proc input-port? (E ) ps) (proc integer->char (E ) (=CL code-char)) (proc integer? (E T ) (=CL integerp)) (proc last-pair (R ) (=CL last)) (proc lcm (R ) CL) (proc length (E T S) CL) (proc list (E T S) CL) (proc list->string (E T ) ps) (proc list->vector (E T ) ps) (proc list-ref (R ) ps) (proc list-tail (R ) ps) (proc load (E T ) ~CL) (proc log (R T ) CL) (proc magnitude (R ) (=CL abs)) (proc make-polar (R ) ps) (proc make-rectangular (R ) (=CL complex)) (proc make-string (E T S) ps) (proc make-vector (E T S) ps) (proc map (R T ) (~CL mapcar)) (proc max (E T S) CL) (proc member (E T S) ps) (proc memq (E T S) ps) (proc memv (E ) (=CL lisp:member)) (proc min (E T S) CL) (proc modulo (R ) (=CL mod)) (proc negative? (E ) (=CL minusp)) (proc newline (E T ) (=CL terpri)) (proc not (E T S) CL) (proc null? (E T S) (=CL null)) (proc number->string (R ) ps) (proc number? (E T S) (=CL numberp)) (proc numerator (R ) CL) (proc odd? (E T ) (=CL oddp)) (proc open-input-file (R ) ps) (proc open-output-file (R ) ps) (proc output-port? (E ) ps) (proc pair? (E T ) (=CL consp)) (proc positive? (E T ) (=CL plusp)) (proc pp ( T M) ps) (proc princ ( S) ~CL) ;; PRINT is incompatible between T and S&ICP. (proc prin1 ( S) CL) (proc procedure? (E ) ps) (proc quotient (E T S) ps) (proc random ( M) CL) (proc rational? (E T ) (=CL rationalp)) (proc rationalize (R ) CL) (proc read-char (E T ) ps) (proc read (E T ) ps) (proc real? (E T ) ps) (proc real-part (R ) (=CL realpart)) (proc remainder (E T S) (=CL rem)) (proc reverse (R T ) CL) (proc round (R ) ~CL) (proc set-car! (E ) ps) (proc set-cdr! (E ) ps) (proc sin (R S) CL) (proc sqrt (R T S) CL) (proc string->list (E T ) ps) (proc string->number (R T ) ps) (proc string->symbol (R T ) ps) (proc string-append (E T ) ps) (proc string-ci<=? (R ) (=CL string-not-greaterp)) (proc string-ci=? (R ) (=CL string-not-lessp)) (proc string-ci>? (R ) (=CL string-greaterp)) (proc string-copy (R ) (=CL copy-seq)) (proc string-fill! (R ) (=CL fill)) (proc string-length (E T ) (=CL length)) (proc string-ref (E ) (=CL char)) (proc string-set! (R ) ps) (proc string<=? (E ) (=CL string<=)) (proc string=? (E ) (=CL string>=)) (proc string>? (E ) (=CL string>)) (proc string? (E T ) (=CL stringp)) (proc substring (E ) (=CL subseq)) (proc symbol->string (E T ) (=CL symbol-name)) (proc symbol? (E T ) ps) (proc tail ( S) ps) (proc tan (R T ) CL) (proc transcript-on (R T ) (=CL dribble)) (proc transcript-off (R T ) (=CL dribble)) (proc truncate (R ) ~CL) (proc vector->list (E T ) ps) (proc vector-fill! (R ) (=CL fill)) (proc vector-length (E T ) (=CL length)) (proc vector-ref (E S) (=CL svref)) (proc vector-set! (E S) ps) (proc vector? (E T ) ps) (proc vector (E T ) CL) (proc with-input-from-file (R ) ps) (proc with-output-to-file (R ) ps) (proc write-char (E ) CL) (proc write (R ) (~CL prin1)) (proc zero? (E T ) (=CL zerop)) ) ;;; Code manipulation utilities. (eval-when (lisp:eval load compile) ;;; Set up for the type syntax extension (defun rest? (params) (and (not (null params)) (or (not (consp params)) (eq (car params) '&rest) (eq (car params) '&body)))) (defun end? (params) (null params)) (defun parameter-name (param) (if (consp param) (car param) param)) (defun rest-parameter (params) (if (consp params) (cadr params) params)) (defun named? (param) param) (defun convert-arglist (params mode &optional (optional-or-key nil)) (cond ((rest? params) (lisp:let* ((p (rest-parameter params)) (name (if (named? p) (parameter-name p) 'ignore))) (ecase mode ((apply) (list (if (named? p) name ''()))) ((no-rest) '()) ((scheme-lambda) name) ((defmacro) ;wonderful hack `(,(if (string= (symbol-name name) "BODY") '&body '&rest) ,name)) ((cl-lambda) `(&rest ,name)) ((the-args) (if (named? p) (list name) '()))))) ((end? params) (ecase mode ((apply) '('())) ((no-rest scheme-lambda defmacro cl-lambda the-args) '()))) (t (lisp:let ((param (car params))) (cond ((lisp:member param '(&optional &key)) (ecase mode ((defmacro cl-lambda) params) ((apply no-rest scheme-lambda the-args) ;; Gross approximation!!! (convert-arglist (cdr params) mode param)))) (t (cons (parameter-name param) (convert-arglist (cdr params) mode optional-or-key)))))))) (defun lambda-expression? (exp) (and (consp exp) (eq (car exp) 'lambda))) (defun definition? (exp) (and (consp exp) (eq (car exp) 'define))) (defun undefinify (body) (do ((e body (cdr e)) (d '() (lisp:let ((pat (cadr (car e))) (bod (cddr (car e)))) (cons (if (consp pat) `(,(car pat) (lambda ,(cdr pat) ,@bod)) (cdr (car e))) d)))) ((or (null (cdr e)) (not (definition? (car e)))) (if (null d) body `((letrec ,(reverse d) ,@e)))))) (defun beginify (forms) (if (null (cdr forms)) (car forms) `(begin ,@forms))) (defun maybe-preprocess (exp) (if *preprocess?* (preprocess-top exp) exp)) ;;; Prevents repeated preprocessing. (defun prevent-preprocessing (forms) `(macrolet ((scheme-expression (&rest form) form)) ,@forms)) (defvar *allow-possibly-incorrect-speed-hacks?* nil) (defun *---*? (var) (lisp:let ((s (symbol-name var))) (and (>= (length s) 3) (char= (aref s 0) #\*) (char= (aref s (- (length s) 1)) #\*)))) );nehw-lave ;;; It would take a while to explain how this hack works. Trust me. (defmacro scheme-expression (-ignore- &rest form) #+Symbolics -ignore- #-Symbolics (declare (ignore -ignore-)) (prevent-preprocessing (list (maybe-preprocess form)))) ;;; Definition forms: DEFINE, DEFINE-MACRO ;;; These can only occur at top level (what about inside a BEGIN?). ;;; Must use DEFVAR to inhibit "not declared special" warnings. (defmacro define (pat &body body) (if (consp pat) `(define ,(car pat) (lambda ,(cdr pat) ,@body)) (lisp:let* ((var pat) (val (maybe-preprocess (car body))) (h (get var 'history))) (cond ((or (lisp:member :setq h) (*---*? var)) `(define[variable] ,var ,val)) ((and *allow-possibly-incorrect-speed-hacks?* (not (lisp:member :function h)) (not (lisp:member :set!-globally h)) (or (and (lambda-expression? val) (not (complicated? val))) (and (symbolp val) (lisp:member (get val 'method) '(:macro :function))))) `(define[subst] ,var ,val)) ((lambda-expression? val) `(define[function] ,var ,val)) ((and *allow-possibly-incorrect-speed-hacks?* (number? val)) `(define[constant] ,var ,val)) (t `(define[variable] ,var ,val)))))) (defmacro define[function] (var (-ignore- vars . body)) #-Symbolics (declare (ignore -ignore-)) #+Symbolics -ignore- (use-method var :function) `(progn 'compile (use-method ',var :function) (defvar ,var) (defun ,var ,(convert-arglist vars 'cl-lambda) (definition-body ,vars ,@body)) (setq ,var #',var) ',var)) (defmacro define[variable] (var val) (use-method var :variable) `(progn 'compile (use-method ',var :variable) (defvar ,var) (setq ,var ,(prevent-preprocessing (list val))) (defun ,var (&rest args) (apply ,var args)))) (defmacro define[constant] (var val) (use-method var :constant) `(progn 'compile (use-method ',var :constant) (defconstant ,var ,(prevent-preprocessing (list val))))) (defmacro define[subst] (pat &body body) (if (consp pat) `(define[subst] ,(car pat) (lambda ,(cdr pat) ,@body)) (lisp:let ((var pat) (val (car body))) (use-method var :macro) (if (lambda-expression? (car body)) (lisp:let* ((vars (cadr val)) (body-exp (if (null (cdddr val)) (caddr val) `(progn ,@(cddr val)))) (aux-var (concatenate-symbol var '[closed])) (the-vars (convert-arglist vars 'the-args))) `(progn 'compile (use-method ',var :macro) (defmacro ,var ,(convert-arglist vars 'defmacro) ,(if (null the-vars) `',body-exp `(sublis (mapcar #'cons ',the-vars (list ,@the-vars)) ',body-exp))) (defvar ,var) (defun ,aux-var ,(convert-arglist vars 'cl-lambda) (definition-body ,vars ,@(cddr val))) (setq ,var #',aux-var) ',var)) `(progn 'compile (use-method ',var :macro) (defmacro ,var (&rest args) `(,',val ,@args)) (defparameter ,var ,(prevent-preprocessing (list val)))))))) (defmacro define-macro (pat &body body) (lisp:let* ((name (car pat)) (kludge (maybe-preprocess `(lambda ,(cdr pat) ,@body))) (bvl (cadr kludge)) (body (cddr kludge))) `(progn 'compile (defmacro ,name ,(convert-arglist bvl 'defmacro) ,@body) (eval-when (lisp:eval load compile) (setf (get ',name 'scheme-macro) t)) ',name))) (defmacro definition-body (vars &body body) (prevent-preprocessing `((lambda-body ,vars ,@body)))) ;;; Pass 2 of Scheme->CL translation is accomplished by a bunch of CL ;;; macros. ;;; Special forms are in alphabetical order (defmacro define-pseudo-macro (pat &body body) (lisp:let* ((name (car pat)) (aux-name (lisp:let ((*package* (find-package 'pseudoscheme))) (concatenate-symbol name '-aux)))) `(progn 'compile (setf (get ',name 'aux-name) ',aux-name) (defmacro ,name ,(convert-arglist (cdr pat) 'defmacro) `(scheme-expression ,',aux-name ,',name ,@(list* ,@(convert-arglist (cdr pat) 'apply)))) (defmacro ,aux-name (-ignore- ,@(convert-arglist (cdr pat) 'defmacro)) #+Symbolics -ignore- #-Symbolics (declare (ignore -ignore-)) ,@body)))) (defmacro begin (&rest forms) ;Allow for (begin (define ...) ...) `(progn 'compile ,@forms)) (defmacro call (&rest args) `(funcall ,@args)) (defmacro jcall (&rest args) `(funcall ,@args)) (define-pseudo-macro (case test-object . body) `(lisp:case ,test-object ,@(mapcar #'(lisp:lambda (clause) (if (eq (car clause) 'else) `(otherwise ,@(cdr clause)) clause)) body))) (define-pseudo-macro (cons-stream hd tl) `(cons ,hd (delay ,tl))) (define-pseudo-macro (delay exp) `(make-delay :thunk-or-value #'(lisp:lambda () ,exp))) ;;; We can't just define a LISP:LAMBDA macro, since the host Common Lisp ;;; might define one of its own that shouldn't be clobberred. (This is ;;; the case on the 3600.) (define-pseudo-macro (lambda vars . body) `#'(lisp:lambda ,(convert-arglist vars 'cl-lambda) (lambda-body ,vars ,@body))) ;;; Used by pass-1 output (define-pseudo-macro (allow-function-references vars . body) (if (null vars) (beginify body) `(macrolet ,(mapcar #'(lisp:lambda (var) `(,var (&rest args) `(call ,',var ,@args))) vars) ,@body))) ;;; Needed by top-level DEFINE; makes bodies of lambda-expressions ;;; prettier for GRINDEF etc. (defmacro lambda-body (vars &body body) #-Symbolics (declare (ignore vars)) (lisp:let* ((body (undefinify body)) #+Symbolics (rest-var (if (atom vars) vars (cdr (last vars))))) #-Symbolics (beginify body) #+Symbolics (if rest-var `(lisp:let ((,rest-var (copy-list ,rest-var))) ,@body) (beginify body)))) (define-pseudo-macro (let specs . body) (cond ((or (null specs) (consp specs)) ;; Normal case `(lisp:let ,specs ,@(undefinify body))) (t ;; Named LET (lisp:let ((tag specs) (specs (car body)) (body (cdr body))) `(,(if *preprocess?* 'letrec 'letrec[tagbody]) ;; If preprocessor is turned off, be optimistic. ((,tag (lambda ,(mapcar #'car specs) ,@body))) (,tag ,@(mapcar #'cadr specs))))))) (define-pseudo-macro (let* specs . body) (cond ((null specs) (beginify body)) (t `(let (,(car specs)) (let* ,(cdr specs) ,@body))))) ;;; There are three kinds of LETREC. In the absence of further ;;; information we assume the worst. ;;; 1. Most general case (define-pseudo-macro (letrec specs . body) `(let ,(mapcar #'(lisp:lambda (spec) `(,(car spec) ':undefined)) specs) (allow-function-references ,(mapcar #'car specs) ,@(mapcar #'(lisp:lambda (spec) `(setq ,@spec)) specs) ,@(undefinify body)))) ;;; 2. Somewhat less general; SET! and non-functional references not ;;; allowed. (defmacro letrec[labels] (specs &body body) `(lisp:labels ,(mapcar #'(lisp:lambda (spec) (lisp:let ((var (car spec)) (val (cadr spec))) (cond ((lambda-expression? val) `(,var ,(convert-arglist (cadr val) 'cl-lambda) (lambda-body ,@(cdr val)))) (t (cerror "ignore it" "losing LETREC spec - ~s" spec) `(,var (&rest foo) foo))))) specs) ,@body)) ;;; 3. This is the interesting case: the LETREC is really a loop. This ;;; only works if all calls to all the procedures are at the same ;;; tail-recursion level. (defmacro letrec[tagbody] (specs &body body) #-TAIL-RECURSION-WINS (lisp:let* ((number-of-registers (apply #'max (mapcar #'(lisp:lambda (spec) (length (cadr (cadr spec)))) specs))) (registers (do ((i 0 (lisp:+ i 1)) (regs '() (cons (gensym "REG-") regs))) ((= i number-of-registers) (reverse regs)))) (block-name (gensym "LETREC-"))) `(lisp:block ,block-name (lisp:let ,registers (macrolet ,(mapcar #'(lisp:lambda (spec) (lisp:let ((tag (car spec)) (vars (cadr (cadr spec)))) `(,tag ,vars `(progn (setq ,@(list ,@(mapcan #'(lisp:lambda (reg var) `(',reg ,var)) registers vars))) (go ,',tag))))) specs) (tagbody (return-from ,block-name ,(beginify (undefinify body))) ,@(mapcan #'(lisp:lambda (spec) (list (car spec) `(return-from ,block-name (let ,(mapcar #'list (cadr (cadr spec)) registers) ,@(undefinify (cddr (cadr spec))))))) specs)))))) #+TAIL-RECURSION-WINS `(letrec[labels] ,specs ,@body)) (define-pseudo-macro (sequence . forms) `(begin ,@forms)) ;;; Note that SET! on lambda-bound variables won't work in ;;; unpreprocessed programs. (define-pseudo-macro (set! var val) (lisp:let ((method (get var 'method))) (cond ((or (null method) (eq method :variable)) ;; Be optimistic. (record-variable-usage var :setq) `(setq ,var ,val)) (t (when (eq method :macro) (cerror "proceed as if everything was OK" "(DEFINE ~S ...) was implemented as (DEFMACRO ~S ...),~ ~%but (SET! ~S ...) was encountered" var var var)) (record-variable-usage var :set!-globally) `(set!-globally ',var ,val))))) (defun set!-globally (var val) (record-variable-usage var :set!-globally) (setf (symbol-value var) val) (if (procedure? val) (setf (symbol-function var) val) (setf (symbol-function var) #'(lisp:lambda (&rest args) (apply var args)))) t) ;;; quasiquote (define-pseudo-macro (quasiquote x) (expand-quasiquote x 0)) (define-pseudo-macro (unquote x) (cerror "act as if the comma wasn't there at all" "comma not inside backquote form - ,~S" x) x) (define-pseudo-macro (unquote-splicing x) (cerror "act as if the ,@ wasn't there at all" "\",@\" not inside backquote form - ,@~S" x) x) (define (expand-quasiquote x level) (multiple-value-bind (mode arg) (descend-quasiquote x level) (finalize-quasiquote mode arg))) (define (finalize-quasiquote mode arg) (cond ((eq mode 'quote) `',arg) ((eq mode 'unquote) arg) ((eq mode 'unquote-splicing) (cerror "act as if () had been seen instead of ,@
" ",@ in illegal context - ,@~s" arg)) (else (cons mode arg)))) ;;; The two return values, mode and arg, are interpreted as follows: ;;; mode arg meaning ;;; QUOTE x 'x ;;; UNQUOTE x x ;;; LIST (x1 x2 ...) (LIST x1 x2 ...) ;;; CONS* (x1 x2 ...) (CONS* x1 x2 ...) ;;; APPEND (x1 x2 ...) (APPEND x1 x2 ...) (define (descend-quasiquote x level) (cond ((vector? x) (descend-quasiquote-vector x level)) ((atom x) (values 'quote x)) ((interesting-to-quasiquote? x 'quasiquote) (descend-quasiquote-pair x (1+ level))) ((interesting-to-quasiquote? x 'unquote) (cond ((= level 0) (values 'unquote (cadr x))) (else ;; BUG: ,,@ doesn't work. I think this is the spot ;; where it would have to be hacked in. (descend-quasiquote-pair x (- level 1))))) ((interesting-to-quasiquote? x 'unquote-splicing) (cond ((= level 0) (values 'unquote-splicing (cadr x))) (else (descend-quasiquote-pair x (- level 1))))) (else (descend-quasiquote-pair x level)))) (define (descend-quasiquote-pair x level) (multiple-value-bind (car-mode car-arg) (descend-quasiquote (car x) level) (multiple-value-bind (cdr-mode cdr-arg) (descend-quasiquote (cdr x) level) (cond ((and (eq car-mode 'quote) (eq cdr-mode 'quote)) (values 'quote x)) ((eq car-mode 'unquote-splicing) ;; (,@mumble ...) (cond ((and (eq cdr-mode 'quote) (null cdr-arg)) (values 'unquote car-arg)) ((eq cdr-mode 'append) (values 'append (cons car-arg cdr-arg))) (else (values 'append (list car-arg (finalize-quasiquote cdr-mode cdr-arg)))))) ((and (eq cdr-mode 'quote) (null cdr-arg)) (values 'list (list (finalize-quasiquote car-mode car-arg)))) ((or (eq cdr-mode 'list) (eq cdr-mode 'cons*)) (values cdr-mode (cons (finalize-quasiquote car-mode car-arg) cdr-arg))) (else (values 'cons* (list (finalize-quasiquote car-mode car-arg) (finalize-quasiquote cdr-mode cdr-arg)))))))) ;;; #(a b c) ==> '#(a b c) ;;; #(a ,b c) ==> (vector 'a b 'c) ;;; #(a ,@b ,c) ==> (list->vector (append '(a) b (list c))) ;;; Isn't there some elegant way to do this? ;;; YES. Parameterize descend-quasiquote-pair. (define (descend-quasiquote-vector x level) (let loop ((i (- (vector-length x) 1)) (modes '()) (args '()) (flag nil)) (cond ((>= i 0) (multiple-value-bind (mode arg) (descend-quasiquote (vector-ref x i) level) ;; mode = quote, unquote, unquote-splicing, list, cons* (loop (- i 1) (cons mode modes) (cons arg args) (or flag (not (eq mode 'quote)))))) ((not flag) (values x 'quote)) ((lisp:member 'unquote-splicing modes) (values 'list->vector (list (cons 'append (mapcar (lambda (mode arg) (cond ((eq mode 'unquote-splicing) arg) ;; Not good. ((eq mode 'quote) (list 'quote (list arg))) (else (list 'list (finalize-quasiquote mode arg))))) modes args))))) (else (values 'vector (mapcar #'finalize-quasiquote modes args)))))) (define (interesting-to-quasiquote? x marker) (and (consp x) (eq (car x) marker) (consp (cdr x)) (null (cddr x)))) ;;;; Procedures (defmacro define-pseudo (pat &body body) `(define ,pat ,@body)) (defmacro define-pseudo[subst] (pat &body body) `(define[subst] ,pat ,@body)) (define-pseudo[subst] (assq obj list) (lisp:assoc obj list :test #'eq)) (define-pseudo[subst] (assoc obj list) (lisp:assoc obj list :test #'equal)) (define-pseudo[subst] (boolean? obj) (or (eq obj t) (eq obj nil))) (define-pseudo[subst] (call-with-current-continuation proc) (block current-continuation (call proc #'(lisp:lambda (val) (return-from current-continuation val))))) (define-pseudo[subst] (call-with-input-file string proc) (with-open-file (port string :direction :input) (call proc port))) (define-pseudo[subst] (call-with-output-file string proc) (with-open-file (port string :direction :output) (call proc port))) (define-pseudo (char-whitespace? char) (or (char= char #\space) (not (graphic-char-p char)))) (define-pseudo[subst] (current-input-port) *standard-input*) (define-pseudo[subst] (current-output-port) *standard-output*) (define-pseudo[subst] (eof-object? obj) (eq obj 'eof-object)) (define-pseudo (error . items) (apply #'cerror "Return from the call to the ERROR procedure" (apply #'concatenate 'string "~a" (mapcar #'(lisp:lambda (item) #-Symbolics (declare (ignore item)) #+Symbolics item "~%~s") (cdr items))) items)) #+LispM (setf (get 'error :error-reporter) t) ;Thanks to KMP (define-pseudo (eval obj env) env (lisp:eval (prevent-preprocessing (list (preprocess-top obj))))) (define-pseudo (exact? num) num nil) (defstruct (delay (:print-function print-delay) (:predicate delay?)) (forced-yet? nil) thunk-or-value) (defun print-delay (obj stream escape?) #-Symbolics (declare (ignore escape?)) #+Symbolics escape? (if (delay-forced-yet? obj) (format stream "#{Forced ~S}" (delay-thunk-or-value obj)) (format stream "#{Delayed}"))) (define-pseudo (force obj) (cond ((delay? obj) (lisp:let ((tv (delay-thunk-or-value obj))) (cond ((delay-forced-yet? obj) tv) (t (lisp:let ((val (funcall tv))) (setf (delay-thunk-or-value obj) val) (setf (delay-forced-yet? obj) t) val))))) (t obj))) (define-pseudo (inexact? num) num t) (define-pseudo (input-port? obj) (and (streamp obj) (input-stream-p obj))) (define-pseudo[subst] (list->string list) (coerce list 'string)) (defvar the-empty-vector '#()) (define-pseudo (list->vector l) (if (null l) the-empty-vector (coerce l 'vector))) (define-pseudo[subst] (list-ref list n) (nth n list)) (define-pseudo[subst] (list-tail list n) (nthcdr n list)) (defvar the-empty-string "") (define-pseudo[subst] (make-polar r th) (* r (cis th))) (define-pseudo (make-string size &optional fill) (cond ((= size 0) the-empty-string) (fill (lisp:make-string size :initial-element fill)) (t (lisp:make-string size)))) (define-pseudo (make-vector size &optional (fill '*uninitialized-vector-element*)) (if (= size 0) the-empty-vector (make-sequence 'vector size :initial-element fill))) (define-pseudo[subst] (memq obj list) (lisp:member obj list :test #'eq)) (define-pseudo[subst] (member obj list) (lisp:member obj list :test #'equal)) (define-pseudo (number->string num format) (if (not (and (consp format) (string= (symbol-name (car format)) "HEUR"))) (cerror "act as if the format was (HEUR)" "unimplemented format: (NUMBER->STRING '~s '~s)" num format)) (write-to-string num)) (define-pseudo[subst] (open-input-file string) (open string :direction :in)) (define-pseudo[subst] (open-output-file string) (open string :direction :out)) (define-pseudo (output-port? obj) (and (streamp obj) (output-stream-p obj))) (define-pseudo (pp obj &optional (port *standard-input*)) (lisp:let ((*print-pretty* t)) (format port "~&") (lisp:print obj port) (values))) (defconstant closures-might-be-conses? (consp (lisp:eval '#'(lisp:lambda (x) x)))) (define-pseudo (procedure? obj) (and (functionp obj) (not (symbolp obj)) (and (not closures-might-be-conses?) (not (consp obj))))) (define-pseudo[subst] (quotient n1 n2) (values (truncate n1 n2))) (define-pseudo (read &optional (port *standard-input*)) (lisp:read port nil 'eof-object)) (define-pseudo (read-char &optional (port *standard-input*)) (lisp:read-char port nil 'eof-object)) (define-pseudo (real? obj) (and (numberp obj) (not (complexp obj)))) (define-pseudo[subst] (set-car! pair obj) (setf (car pair) obj)) (define-pseudo[subst] (set-cdr! pair obj) (setf (cdr pair) obj)) (define-pseudo[subst] (string->list string) (coerce string 'list)) (define-pseudo (string->number string exactness radix) exactness (let ((*read-base* (case radix ((b) 2) ((o) 8) ((d) 10) ((x) 16) (else (error "bad radix arg to STRING->NUMBER" radix))))) (with-input-from-string (s string) (read s)))) ;very crude approximation (define-pseudo[subst] (string->symbol string) (values (intern string))) (define-pseudo (string-append . strings) (apply #'concatenate 'string strings)) (define-pseudo[subst] (string-set! s k obj) (setf (aref (the simple-string s) k) obj)) (define-pseudo (symbol? x) (and x (symbolp x) (not (eq x t)))) (define-pseudo[subst] (tail stream) (force (cdr stream))) (define-pseudo the-empty-stream '()) (define-pseudo user-initial-environment nil) (define-pseudo (vector? obj) (and (simple-vector-p obj) ;; Strings are simple vectors in CLISP (this is a bug) #+tops-20 (not (stringp obj)) ;; Structures are vectors in CLISP, bolixlisp, etc. #+(or tops-20 symbolics vax) (not (typep obj 'structure)))) (define-pseudo[subst] (vector->list vec) (coerce vec 'list)) (define-pseudo[subst] (vector-set! vec k obj) (setf (svref vec k) obj)) (define-pseudo[subst] (with-input-from-file string thunk) (with-open-file (*standard-input* string :direction :input) (call thunk))) (define-pseudo[subst] (with-output-to-file string thunk) (with-open-file (*standard-output* string :direction :output) (call thunk))) ;;; Pass 1: Scheme -> readable-CL [preprocessor] ;;; Pass 2: readable-CL -> executable-CL [macros] ;;; Preprocessor to allow for combinations whose cars aren't symbols. ;;; There's the potential for doing a lot of translations and ;;; optimizations in a Scheme->CL preprocessor, but the idea is that we ;;; want to as little translation as possible in order that ;;; pretty-printing and debugging can work as smoothly as possible. Too ;;; much translation was the downfall of the previous Scheme-in-CL ;;; implementation: programs were impossible to debug. ;;; Be careful -- the output of the preprocessor is Common Lisp, not ;;; Scheme. Don't call the preprocessor twice on a given piece of ;;; code. ;;; - Global SET!'s must clobber the function cell, but local ones don't. ;;; - Annotate binding constructs to say when MACROLET's are needed for ;;; variables occurring in function position. ;;; - Detect absence of SET! and do some rudimentary tail recursion ;;; analysis for optimization of LETREC. ;;; The rudiments of global tail recursion processing are here (CALL vs. ;;; JCALL) but don't imagine that tail recursion can be easily added. ;;; Two reasons: ;;; (1) JCALL isn't inserted in all the places it needs to be, and ;;; (2) it might be inserted in some places where it shouldn't be (I'm ;;; not sure). ;;; Bindings ;;; For each variable, we want to know: ;;; Does it occur in value position? ;;; Does it occur in function position? ;;; If so, does it appear with a consistent continuation? ;;; Does it occur in a SET! ? ;;; ENV is a list of (name val? fun? set?). (defstruct (binding (:conc-name lisp:nil) (:predicate lisp:nil) (:copier lisp:nil) (:constructor make-binding (old-name new-name))) old-name new-name (value-references? nil) (function-references? nil) (assigned? nil) (continuation-when-called nil)) (define[subst] (bind-var var new-var env) (cons (make-binding var new-var) env)) (define (lookup name env) (locally (declare (optimize (speed 3) (safety 0))) (find name env :key #'old-name))) (define[subst] (set-value-references?! binding) (setf (value-references? binding) t)) (define[subst] (set-function-references?! binding) (setf (function-references? binding) t)) (define[subst] (set-assigned?! binding) (setf (assigned? binding) t)) (define (set-continuation-when-called! binding want-k) (let ((have-k (continuation-when-called binding))) (cond ((null? have-k) (setf (continuation-when-called binding) want-k)) ((not (eq? have-k want-k)) (setf (continuation-when-called binding) 'inconsistent))))) ;;; Preprocessor starts here. (define (preprocess-top exp) (preprocess exp '() '(value))) (define (preprocess exp env k) (cond ((symbol? exp) (let ((probe (lookup exp env))) (cond ((not probe) (record-variable-usage exp :variable) exp) ;Free (i.e. global) variable (t (set-value-references?! probe) (new-name probe))))) ((not (pair? exp)) exp) ((not (symbol? (car exp))) `(,(if (eq? (car k) 'return) 'jcall 'call) ,@(preprocess-list exp env))) ((get (car exp) 'scheme-macro) (preprocess (macroexpand-1 exp) env k)) (else (case (car exp) ((begin sequence) `(,(car exp) ,@(preprocess-sequence (cdr exp) env k))) ((case) `(case ,(preprocess (cadr exp) env '(value)) ,@(map (lambda (clause) `(,(car clause) ,@(preprocess-sequence (cdr clause) env k))) (cddr exp)))) ((cond) `(cond ,@(map (lambda (clause) (cons (preprocess (car clause) env '(value)) (if (null? (cdr clause)) '() (preprocess-sequence (cdr clause) env k)))) (cdr exp)))) ((do) (preprocess-do exp env k)) ((lambda) (let* ((vars (convert-arglist (cadr exp) 'the-args)) (new-vars (lexical-names vars)) (env (bind-vars vars new-vars env)) (body (preprocess-body (cddr exp) vars env (if (eq? (car k) 'letrec) (cadr k) '(return))))) `(lambda ,(lexical-names (cadr exp)) ,@body))) ((let) (preprocess-let exp env k)) ((let*) (preprocess-let* exp env k)) ((letrec) (preprocess-letrec exp env k)) ((quasiquote) (preprocess (expand-quasiquote (cadr exp) 0) env k)) ((quote) exp) ((set!) (let* ((var (cadr exp)) (val (preprocess (caddr exp) env exp)) (probe (lookup var env))) (cond (probe (set-assigned?! probe) ;assigned `(setq ,(new-name probe) ,val)) (else `(set! ,var ,val))))) ((if) `(if ,(preprocess (cadr exp) env '(value)) ,@(map (lambda (exp) (preprocess exp env k)) (cddr exp)))) ((and or) `(,(car exp) ,@(if (null? (cdr exp)) '() (preprocess-sequence (cdr exp) env k)))) ((cons-stream delay) `(,(car exp) ,@(preprocess-list exp env))) ((macrolet progn function lisp:let lisp:let* define setq call jcall) (cerror "use the expression without preprocessing it" "bogus input for Scheme->CL preprocessor -- ~s" exp) exp) (else (let* ((var (car exp)) (probe (lookup var env)) (args (preprocess-list (cdr exp) env))) (cond (probe (set-function-references?! probe) (set-continuation-when-called! probe k) `(,(new-name probe) ,@args)) ((or (memq (get var 'method) '(:variable :constant)) (memq :setq (get var 'history))) (record-variable-usage var :variable) `(,(if (eq? (car k) 'return) 'jcall 'call) ,var ,@args)) (t (if (eq? (get var 'method) :macro) (record-variable-usage var :macro) (record-variable-usage var :function)) `(,var ,@args))))))))) (define (preprocess-let exp env k) (if (symbol? (cadr exp)) (let ((tag (cadr exp)) (bindings (caddr exp))) (preprocess `(letrec ((,tag (lambda ,(map car bindings) ,@(cdddr exp)))) (,tag ,@(map cadr bindings))) env k)) (let* ((vars (map car (cadr exp))) (new-vars (lexical-names vars)) (bindings (preprocess-bindings (cadr exp) new-vars env)) (env (bind-vars vars new-vars env)) (body (preprocess-body (cddr exp) vars env k))) `(let ,bindings ,@body)))) (define (preprocess-bindings bindings new-vars env) (map (lambda (spec new-var) `(,new-var ,(preprocess (cadr spec) env '(value)))) bindings new-vars)) (define (preprocess-let* exp env k) (let loop ((old (cadr exp)) (new '()) (vars '()) (env env)) (if (null? old) `(let* ,(reverse new) ,@(preprocess-body (cddr exp) vars env k)) (let* ((var (caar old)) (new-var (lexical-name var))) (loop (cdr old) (cons (cons new-var (preprocess-region (cdar old) vars env '(value))) new) (cons var vars) (bind-var var new-var env)))))) ;;; For an input expression (letrec ((f (arg) f-body)) body) we can generate ;;; any of three possible outputs. In order of decreasing generality: ;;; ;;; Pass-1 (preproc.) Pass-2 output (macroexpansion) ;;; output ;;; ;;; 1. (letrec ...) (let ((f :undefined)) ;;; (allow-function-references (f) ;;; (setq f #'(lambda ...)) ;;; ...)) ;;; ;;; 2. (letrec[labels] ...) (labels ((f ...)) ...) ;;; ;;; 3. (letrec[tagbody] ...) (let ((reg)) ;;; (tagbody ... f ... (setq reg ...) (go f) ...)) (define (preprocess-letrec exp env k) (let* ((vars (map car (cadr exp))) (new-vars (lexical-names vars)) (env (bind-vars vars new-vars env)) (unique (list 'letrec-return)) (binding-cont (list 'letrec unique)) (bindings (map (lambda (spec new-var) `(,new-var ,(preprocess (cadr spec) env binding-cont))) (cadr exp) new-vars)) (body (preprocess-sequence (undefinify (cddr exp)) env unique)) (type (cond ((null? bindings) 'let) ((not (and (every (lambda (var) (let ((z (lookup var env))) ;; All refs are function refs. (and (not (value-references? z)) (not (assigned? z))))) vars) (every (lambda (binding) (lambda-expression? (cadr binding))) bindings))) 'letrec) ((every (lambda (var) (let ((z (lookup var env))) (eq? (continuation-when-called z) unique))) vars) (for-each (lambda (var) (let ((z (lookup var env))) ;; Permit nested lops (set-continuation-when-called! z k))) vars) 'letrec[tagbody]) (else `letrec[labels])))) `(,type ,bindings ,@body))) (define (preprocess-do exp env k) (let* ((specs (cadr exp)) (vars (map car specs)) (new-vars (lexical-names vars)) (new-env (bind-vars vars new-vars env)) (end (caddr exp))) `(do ,(map (lambda (spec new-var) `(,new-var ,(preprocess (cadr spec) env '(value)) ,@(if (null? (cddr spec)) '() (preprocess-region (cddr spec) vars new-env '(value))))) specs new-vars) (,@(preprocess-region (list (car end)) vars new-env '(value)) ,@(preprocess-region (cdr end) vars new-env k)) ,@(if (null? (cdddr exp)) '() (preprocess-region (cdddr exp) vars new-env '(ignore)))))) (define (preprocess-sequence exp-list env k) (cond ((null? (cdr exp-list)) (list (preprocess (car exp-list) env k))) (else ;; Continuation can't be IGNORE -- this routine is also used ;; by AND, OR, and DO (cons (preprocess (car exp-list) env '(value)) (preprocess-sequence (cdr exp-list) env k))))) (define (preprocess-list exp-list env) (map (lambda (exp) (preprocess exp env '(value))) exp-list)) (define (preprocess-body body vars env k) (preprocess-region (undefinify body) vars env k)) ;;; Allow function references to VARS within the sequence BODY. ;;; The output is never a longer list than the input. (define (preprocess-region body vars env k) (let* ((body (preprocess-sequence body env k)) ;side-affects env (vars (function-vars vars env))) (if vars `((allow-function-references ,(convert-arglist vars 'no-rest) ,@body)) body))) (define (function-vars vars env) (do ((v vars (cdr v)) (f '() ;; A VAX LISP bug prevents the use of LET here. (lisp:let ((z (lookup (car v) env))) (if (function-references? z) (cons (new-name z) f) f)))) ((null? v) f))) (define (lexical-names vars) ;deal with dotted (cond ((pair? vars) (cons (lexical-name (car vars)) (lexical-names (cdr vars)))) ((symbol? vars) (lexical-name vars)) (else vars))) (define (bind-vars vars new-vars env) (do ((vars vars (cdr vars)) (new-vars new-vars (cdr new-vars)) (env env (bind-var (car vars) (car new-vars) env))) ((null? vars) env))) ;;; Gross kludge for guessing when it's OK to substitute. (define (proper-list? thing) (or (null? thing) (and (pair? thing) (null? (cdr (last-pair thing)))))) (define (complicated? lambda-exp) (let ((vars (cadr lambda-exp)) (body (cddr lambda-exp)) (var-count 0)) (or (not (proper-list? vars)) (letrec ((count (lambda (exp) (cond ((pair? exp) (do ((n 0 (+ n (count (car l)))) (l exp (cdr l))) ((null? l) n))) ((memq exp vars) (set! var-count (+ var-count 1))) (t 1))))) (or (> (count body) 10) (> var-count (length vars))))))) ;;; Adapted from OZ:METAP.LISP.2. Thanks to DAM for ;;; figuring this out. #+Symbolics (progn 'compile (setf (get 'define 'zwei:definition-function-spec-type) 'defun) (setf (get 'define-macro 'zwei:definition-function-spec-type) 'defun) (setf (get 'define-pseudo 'zwei:definition-function-spec-type) 'defun) (setf (get 'define-pseudo-macro 'zwei:definition-function-spec-type) 'defun) ;The value of the property ZWEI:DEFINITION-FUNCTION-SPEC-FINDER ;should be a function which takes the ZWEI point after the definition ;symbol (e.g. DEFINE) and returns the point at the begining of the fspec. ;See the function ZWEI:GET-DEFINITION-FUNCTION-SPEC ;DEFINE-FSPEC-FINDER goes forward to the begining of the next atom. ;This means skipping white space and left parenthesis. (defun define-fspec-finder (bp) (zwei:forward-over (cons (if (numberp (car zwei:*whitespace-chars*)) 40. #\() zwei:*whitespace-chars*) bp)) (setf (get 'define 'zwei:definition-function-spec-finder) #'define-fspec-finder) (setf (get 'define-macro 'zwei:definition-function-spec-finder) #'define-fspec-finder) (setf (get 'define-pseudo 'zwei:definition-function-spec-finder) #'define-fspec-finder) (setf (get 'define-pseudo-macro 'zwei:definition-function-spec-finder) #'define-fspec-finder) ) #+Symbolics (zl:setq-standard-value si:*prinarray* t) #-Symbolics (setq *print-array* t) (defun quasiquote-read-macro (stream c) #+Symbolics c #-Symbolics (declare (ignore c)) (list 'quasiquote (lisp:read stream t nil t))) (defun unquote-read-macro (stream c) #+Symbolics c #-Symbolics (declare (ignore c)) (list (lisp:let ((following-char (lisp:peek-char nil stream nil stream t))) (cond ((char= following-char #\@) (read-char stream) 'unquote-splicing) (t 'unquote))) (lisp:read stream t nil t))) (set-macro-character #\` #'quasiquote-read-macro) (set-macro-character #\, #'unquote-read-macro) #+Symbolics ;This stuff still doesn't make PP work. (progn 'compile (zl:defprop quasiquote grind-quasiquote si:grind-macro) (defun grind-quasiquote (exp loc) loc (si:gtyo #.(zl:character (char-code #\`))) (si:grind-form (cadr exp) (zl:locf (cadr exp)))) (zl:defprop unquote grind-unquote si:grind-macro) (defun grind-unquote (exp loc) loc (si:gtyo #.(zl:character (char-code #\,))) (si:grind-form (cadr exp) (zl:locf (cadr exp)))) (zl:defprop unquote-splicing grind-unquote-splicing si:grind-macro) (defun grind-unquote-splicing (exp loc) loc (si:gtyo #.(zl:character (char-code #\,))) (si:gtyo #.(zl:character (char-code #\@))) (si:grind-form (cadr exp) (zl:locf (cadr exp)))) ) #+(and VAX VMS) (progn 'compile (system::define-list-print-function quasiquote (list stream) (declare (list list)) (if (= (length list) 2) (format stream "`~W" (second list)) (format stream "~1!~@{~W~^ ~:_~}~." list))) (system::define-list-print-function unquote (list stream) (declare (list list)) (if (= (length list) 2) (format stream ",~W" (second list)) (format stream "~1!~@{~W~^ ~:_~}~." list))) (system::define-list-print-function unquote-splicing (list stream) (declare (list list)) (if (= (length list) 2) (format stream ",@~W" (second list)) (format stream "~1!~@{~W~^ ~:_~}~." list))) );ngorp (defvar *scheme-source-file-type* "SCM") #+Symbolics (lisp:let ((type (zl:string *scheme-source-file-type*))) (cond ((not (lisp:member type fs:*its-uninteresting-types* :test #'equal)) (push type fs:*its-uninteresting-types*)))) #+Symbolics (fs:define-canonical-type :scheme *scheme-source-file-type*) ;Scheme source ;;; Default mode for scheme source is lisp. #+Symbolics (unless (assoc :scheme fs:*file-type-mode-alist*) (push (cons :scheme :lisp) fs:*file-type-mode-alist*)) ;;; (defun declare-pseudoscheme-package (pkg) #-Symbolics (declare (ignore pkg)) #+Symbolics (pushnew (if (packagep pkg) pkg (find-package pkg)) si:*reasonable-packages*) t) (defun scheme () #+Symbolics (zl:setq-standard-value zl:package (find-package 'scheme)) #-Symbolics (set! *package* (find-package 'scheme)) 'scheme) (setq *preprocess?* t) (provide "pseudoscheme") (in-package 'scheme :use '(pseudoscheme)) (declare-pseudoscheme-package 'scheme)