0.7.9.9:
[sbcl.git] / src / code / eval.lisp
index 23af789..f8ef5b0 100644 (file)
@@ -9,43 +9,50 @@
 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
 ;;;; files for more information.
 
 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
 ;;;; files for more information.
 
-(in-package "SB!BYTECODE")
-
-;;; This needs to be initialized in the cold load, since the top-level
-;;; catcher will always restore the initial value.
-(defvar *eval-stack-top* 0)
+(in-package "SB!IMPL")
 
 ;;; general case of EVAL (except in that it can't handle toplevel
 
 ;;; general case of EVAL (except in that it can't handle toplevel
-;;; EVAL-WHEN magic properly): Delegate to the byte compiler.
-(defun %eval (expr)
-  (funcall (compile (gensym "EVAL-TMPFUN-")
-                   `(lambda ()
-
-                      ;; SPEED=0,DEBUG=1 => byte-compile
-                      (declare (optimize (speed 0) (debug 1))) 
-
-                      ;; Other than that, basically we care about
-                      ;; compilation speed, compilation speed, and
-                      ;; compilation speed. (There are cases where
-                      ;; the user wants something else, but we don't
-                      ;; know enough to guess that; and if he is
-                      ;; unhappy about our guessed emphasis, he
-                      ;; should explicitly compile his code, with
-                      ;; explicit declarations to tell us what to
-                      ;; emphasize.)
-                      (declare (optimize (space 1) (safety 1)))
-                      (declare (optimize (compilation-speed 3)))
+;;; EVAL-WHEN magic properly): Delegate to #'COMPILE.
+(defun %eval (expr lexenv)
+  (funcall (sb!c:compile-in-lexenv
+            (gensym "EVAL-TMPFUN-")
+            `(lambda ()
+               ,expr)
+            lexenv)))
 
 
-                      ,expr))))
+;;; Handle PROGN and implicit PROGN.
+(defun eval-progn-body (progn-body lexenv)
+  (unless (list-with-length-p progn-body)
+    (let ((*print-circle* t))
+      (error 'simple-program-error
+            :format-control
+            "~@<not a proper list in PROGN or implicit PROGN: ~2I~_~S~:>"
+            :format-arguments (list progn-body))))
+  ;; Note:
+  ;;   * We can't just use (MAP NIL #'EVAL PROGN-BODY) here, because we
+  ;;     need to take care to return all the values of the final EVAL.
+  ;;   * It's left as an exercise to the reader to verify that this
+  ;;     gives the right result when PROGN-BODY is NIL, because
+  ;;     (FIRST NIL) = (REST NIL) = NIL.
+  (do* ((i progn-body rest-i)
+       (rest-i (rest i) (rest i)))
+      (nil)
+    (if rest-i ; if not last element of list
+       (eval-in-lexenv (first i) lexenv)
+       (return (eval-in-lexenv (first i) lexenv)))))
 
 
-;;; Pick off a few easy cases, and the various top-level EVAL-WHEN
-;;; magical cases, and call %EVAL for the rest. 
 (defun eval (original-exp)
   #!+sb-doc
   "Evaluate the argument in a null lexical environment, returning the
   result or results."
 (defun eval (original-exp)
   #!+sb-doc
   "Evaluate the argument in a null lexical environment, returning the
   result or results."
+  (eval-in-lexenv original-exp (make-null-lexenv)))
+
+;;; Pick off a few easy cases, and the various top level EVAL-WHEN
+;;; magical cases, and call %EVAL for the rest.
+(defun eval-in-lexenv (original-exp lexenv)
   (declare (optimize (safety 1)))
   (declare (optimize (safety 1)))
-  (let ((exp (macroexpand original-exp)))
+  ;; (aver (lexenv-simple-p lexenv))
+  (let ((exp (macroexpand original-exp lexenv)))
     (typecase exp
       (symbol
        (ecase (info :variable :kind exp)
     (typecase exp
       (symbol
        (ecase (info :variable :kind exp)
         ;; compatibility, it can be implemented with
         ;; DEFINE-SYMBOL-MACRO, keeping the code walkers happy.
         (:alien
         ;; compatibility, it can be implemented with
         ;; DEFINE-SYMBOL-MACRO, keeping the code walkers happy.
         (:alien
-         (%eval original-exp))))
+         (%eval original-exp lexenv))))
       (list
        (let ((name (first exp))
       (list
        (let ((name (first exp))
-            (args (1- (length exp))))
+            (n-args (1- (length exp))))
         (case name
         (case name
-          (function
-           (unless (= args 1)
+          ((function)
+           (unless (= n-args 1)
              (error "wrong number of args to FUNCTION:~% ~S" exp))
            (let ((name (second exp)))
              (error "wrong number of args to FUNCTION:~% ~S" exp))
            (let ((name (second exp)))
-             (if (or (atom name)
-                     (and (consp name)
-                          (eq (car name) 'setf)))
+             (if (and (or (atom name)
+                           (and (consp name)
+                                (eq (car name) 'setf)))
+                       (not (consp (let ((sb!c:*lexenv* lexenv))
+                                     (sb!c:lexenv-find name funs)))))
                  (fdefinition name)
                  (fdefinition name)
-                 (%eval original-exp))))
-          (quote
-           (unless (= args 1)
+                 (%eval original-exp lexenv))))
+          ((quote)
+           (unless (= n-args 1)
              (error "wrong number of args to QUOTE:~% ~S" exp))
            (second exp))
           (setq
              (error "wrong number of args to QUOTE:~% ~S" exp))
            (second exp))
           (setq
-           (unless (evenp args)
+           (unless (evenp n-args)
              (error "odd number of args to SETQ:~% ~S" exp))
              (error "odd number of args to SETQ:~% ~S" exp))
-           (unless (zerop args)
+           (unless (zerop n-args)
              (do ((name (cdr exp) (cddr name)))
                  ((null name)
                   (do ((args (cdr exp) (cddr args)))
              (do ((name (cdr exp) (cddr name)))
                  ((null name)
                   (do ((args (cdr exp) (cddr args)))
                     (set (first args) (eval (second args)))))
                (let ((symbol (first name)))
                  (case (info :variable :kind symbol)
                     (set (first args) (eval (second args)))))
                (let ((symbol (first name)))
                  (case (info :variable :kind symbol)
-                   ;; FIXME: I took out the *TOP-LEVEL-AUTO-DECLARE*
-                   ;; test here, and removed the *TOP-LEVEL-AUTO-DECLARE*
+                   ;; FIXME: I took out the *TOPLEVEL-AUTO-DECLARE*
+                   ;; test here, and removed the *TOPLEVEL-AUTO-DECLARE*
                    ;; variable; the code should now act as though that
                    ;; variable is NIL. This should be tested..
                    (:special)
                    ;; variable; the code should now act as though that
                    ;; variable is NIL. This should be tested..
                    (:special)
-                   (t (return (%eval original-exp))))))))
+                   (t (return (%eval original-exp lexenv))))))))
           ((progn)
           ((progn)
-           (when (> args 0)
-             (dolist (x (butlast (rest exp)) (eval (car (last exp))))
-               (eval x))))
+           (eval-progn-body (rest exp) lexenv))
           ((eval-when)
           ((eval-when)
-           (if (and (> args 0)
-                    (or (member 'eval (second exp))
-                        (member :execute (second exp))))
-               (when (> args 1)
-                 (dolist (x (butlast (cddr exp)) (eval (car (last exp))))
-                   (eval x)))
-               (%eval original-exp)))
+           ;; FIXME: DESTRUCTURING-BIND returns ARG-COUNT-ERROR
+           ;; instead of PROGRAM-ERROR when there's something wrong
+           ;; with the syntax here (e.g. missing SITUATIONS). This
+           ;; could be fixed by hand-crafting clauses to catch and
+           ;; report each possibility, but it would probably be
+           ;; cleaner to write a new macro
+           ;; DESTRUCTURING-BIND-PROGRAM-SYNTAX which does
+           ;; DESTRUCTURING-BIND and promotes any mismatch to
+           ;; PROGRAM-ERROR, then to use it here and in (probably
+           ;; dozens of) other places where the same problem arises.
+           (destructuring-bind (eval-when situations &rest body) exp
+             (declare (ignore eval-when))
+             (multiple-value-bind (ct lt e)
+                 (sb!c:parse-eval-when-situations situations)
+               ;; CLHS 3.8 - Special Operator EVAL-WHEN: The use of
+               ;; the situation :EXECUTE (or EVAL) controls whether
+               ;; evaluation occurs for other EVAL-WHEN forms; that
+               ;; is, those that are not top level forms, or those in
+               ;; code processed by EVAL or COMPILE. If the :EXECUTE
+               ;; situation is specified in such a form, then the
+               ;; body forms are processed as an implicit PROGN;
+               ;; otherwise, the EVAL-WHEN form returns NIL.
+               (declare (ignore ct lt))
+               (when e
+                 (eval-progn-body body lexenv)))))
+          ((locally)
+           (multiple-value-bind (body decls) (parse-body (rest exp) nil)
+             (let ((lexenv
+                    ;; KLUDGE: Uh, yeah.  I'm not anticipating
+                    ;; winning any prizes for this code, which was
+                    ;; written on a "let's get it to work" basis.
+                    ;; These seem to be the variables that need
+                    ;; bindings for PROCESS-DECLS to work
+                    ;; (*FREE-FUNS* and *FREE-VARS* so that
+                    ;; references to free functions and variables in
+                    ;; the declarations can be noted;
+                    ;; *UNDEFINED-WARNINGS* so that warnings about
+                    ;; undefined things can be accumulated [and then
+                    ;; thrown away, as it happens]). -- CSR, 2002-10-24
+                    (let ((sb!c:*lexenv* lexenv)
+                          (sb!c::*free-funs* (make-hash-table :test 'equal))
+                          (sb!c::*free-vars* (make-hash-table :test 'eq))
+                          (sb!c::*undefined-warnings* nil))
+                      (sb!c::process-decls decls
+                                           nil nil
+                                           (sb!c::make-continuation)
+                                           lexenv))))
+               (eval-progn-body body lexenv))))
+          ((macrolet)
+           (destructuring-bind (definitions &rest body)
+               (rest exp)
+             ;; FIXME: shared code with FUNCALL-IN-FOOMACROLET-LEXENV
+             (declare (type list definitions))
+             (unless (= (length definitions)
+                        (length (remove-duplicates definitions :key #'first)))
+               (style-warn "duplicate definitions in ~S" definitions))
+             (let ((lexenv
+                    (sb!c::make-lexenv
+                     :default lexenv
+                     :funs (mapcar
+                            (sb!c::macrolet-definitionize-fun
+                             :eval
+                             ;; I'm not sure that this is the correct
+                             ;; LEXENV to be compiling local macros
+                             ;; in...
+                             lexenv)
+                            definitions))))
+               (eval-in-lexenv `(locally ,@body) lexenv))))
+          ((symbol-macrolet)
+           (destructuring-bind (definitions &rest body)
+               (rest exp)
+             ;; FIXME: shared code with FUNCALL-IN-FOOMACROLET-LEXENV
+             (declare (type list definitions))
+             (unless (= (length definitions)
+                        (length (remove-duplicates definitions :key #'first)))
+               (style-warn "duplicate definitions in ~S" definitions))
+             (let ((lexenv
+                    (sb!c::make-lexenv
+                     :default lexenv
+                     :vars (mapcar
+                            (sb!c::symbol-macrolet-definitionize-fun
+                             :eval)
+                            definitions))))
+               (eval-in-lexenv `(locally ,@body) lexenv))))
           (t
            (if (and (symbolp name)
                     (eq (info :function :kind name) :function))
                (collect ((args))
           (t
            (if (and (symbolp name)
                     (eq (info :function :kind name) :function))
                (collect ((args))
-                 (dolist (arg (rest exp))
-                   (args (eval arg)))
-                 (apply (symbol-function name) (args)))
-               (%eval original-exp))))))
+                         (dolist (arg (rest exp))
+                           (args (eval-in-lexenv arg lexenv)))
+                         (apply (symbol-function name) (args)))
+               (%eval exp lexenv))))))
       (t
        exp))))
       (t
        exp))))
-
-;;; Given a function, return three values:
-;;; 1] A lambda expression that could be used to define the function,
-;;;    or NIL if the definition isn't available.
-;;; 2] NIL if the function was definitely defined in a null lexical
-;;;    environment, and T otherwise.
-;;; 3] Some object that \"names\" the function. Although this is
-;;;    allowed to be any object, CMU CL always returns a valid
-;;;    function name or a string.
-;;;
-;;; If interpreted, use the interpreter interface. Otherwise, see
-;;; whether it was compiled with COMPILE. If that fails, check for an
-;;; inline expansion.
-(defun function-lambda-expression (fun)
-  (declare (type function fun))
-  (let* ((fun (%function-self fun))
-        (name (%function-name fun))
-        (code (sb!di::function-code-header fun))
-        (info (sb!kernel:%code-debug-info code)))
-    (if info
-       (let ((source (first (sb!c::compiled-debug-info-source info))))
-         (cond ((and (eq (sb!c::debug-source-from source) :lisp)
-                     (eq (sb!c::debug-source-info source) fun))
-                (values (second (svref (sb!c::debug-source-name source) 0))
-                        nil name))
-               ((stringp name)
-                (values nil t name))
-               (t
-                (let ((exp (info :function :inline-expansion name)))
-                  (if exp
-                      (values exp nil name)
-                      (values nil t name))))))
-       (values nil t name))))
 \f
 ;;; miscellaneous full function definitions of things which are
 ;;; ordinarily handled magically by the compiler
 \f
 ;;; miscellaneous full function definitions of things which are
 ;;; ordinarily handled magically by the compiler