;;;; functions used to parse function/macro bodies
;;;;
;;;; FIXME: In an early attempt to bootstrap SBCL, this file
;;;; was loaded before fundamental things like DEFUN and AND and OR
;;;; were defined, and it still bears scars from the attempt to
;;;; make that work. (TAGBODY, forsooth..) It should be cleaned up.

;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.

(in-package "SB!INT")

(/show0 "entering parse-body.lisp")

;;; Given a sequence of declarations (and possibly a documentation
;;; string) followed by other forms (as occurs in the bodies of DEFUN,
;;; DEFMACRO, etc.) return (VALUES FORMS DECLS DOC), where DECLS holds
;;; declarations, DOC holds a doc string (or NIL if none), and FORMS
;;; holds the other forms.
;;;
;;; If DOC-STRING-ALLOWED is NIL, then no forms will be treated as
;;; documentation strings.
(defun parse-body (body &key (doc-string-allowed t) (toplevel nil))
  (let ((reversed-decls nil)
        (forms body)
        (doc nil))
    ;; Since we don't have macros like AND, OR, and NOT yet, it's hard
    ;; to express these tests clearly. Giving them names seems to help
    ;; a little bit.
    (flet ((doc-string-p (x remaining-forms)
             (if (stringp x)
                 (if doc-string-allowed
                     ;; ANSI 3.4.11 explicitly requires that a doc
                     ;; string be followed by another form (either an
                     ;; ordinary form or a declaration). Hence:
                     (if remaining-forms
                         (if doc
                             ;; ANSI 3.4.11 says that the consequences of
                             ;; duplicate doc strings are unspecified.
                             ;; That's probably not something the
                             ;; programmer intends. We raise an error so
                             ;; that this won't pass unnoticed.
                             (error "duplicate doc string ~S" x)
                             t)))))
           (declaration-p (x)
             (if (consp x)
                 (let ((name (car x)))
                   (case name
                     ((declare) t)
                     ((declaim)
                      (unless toplevel
                        ;; technically legal, but rather unlikely to
                        ;; be what the user meant to do...
                        (style-warn
                         "DECLAIM where DECLARE was probably intended")
                        nil))
                     (t nil))))))
      (tagbody
        :again
        (if forms
            (let ((form1 (first forms)))
              ;; Note: The (IF (IF ..) ..) stuff is because we don't
              ;; have the macro AND yet.:-|
              (if (doc-string-p form1 (rest forms))
                  (setq doc form1)
                  (if (declaration-p form1)
                      (setq reversed-decls
                            (cons form1 reversed-decls))
                      (go :done)))
              (setq forms (rest forms))
              (go :again)))
        :done)
      (values forms
              (nreverse reversed-decls)
              doc))))

(/show0 "leaving parse-body.lisp")
