;;; -*- Mode:LISP; Package:SYSTEM-INTERNALS; Base:10; Readtable:CL -*- ;;; ;;; EXPAND-DEFMACRO-FOR-K ;;; ;;; This is needed by the compiler to correctly expand DEFMACRO forms for ;;; inclusion in KFASL files. (defun expand-defmacro-for-k (x env &optional extra-declarations) (declare (ignore env)) ;Until macroexpand works (let ((name (first x)) (lambda-list (second x)) (body (cddr x)) (macro-form (gentemp 'MACROFORM)) (macro-env (gentemp 'MACROENV))) (multiple-value-bind (body decls doc-string) (GOBBLE-DECLARATIONS body T NIL) ;make this last argument env when macroexpand works. (when doc-string (push `(DOCUMENTATION ,doc-string) decls)) `(NAMED-LAMBDA ,name (,macro-form &OPTIONAL ,macro-env) (DECLARE ,@decls) ,macro-env (,(if (symbolp name) 'BLOCK 'PROGN) ,(if (symbolp name) name NIL) (LET* ,(make-defmacro-let-bindings lambda-list macro-form macro-env) (DECLARE ,@decls) ,@extra-declarations ,@body)))))) (defun make-defmacro-let-bindings (lambda-list macro-form macro-env) (let ((bindings NIL)) (flet ((add-binding (binding) (push binding bindings))) (make-let-bindings-for-nested-lambda-list lambda-list `(CDR ,macro-form) #'add-binding macro-form macro-env)) (reverse bindings))) (defun make-let-bindings-for-nested-lambda-list (lambda-list path push-proc macro-form macro-env) (let ((index 0)) (flet ( ;; This is mapped onto required parameters. (make-binding-or-recurse (var) (cond ((symbolp var) (funcall push-proc `(,var (NTH ,index ,path))) (incf index)) ((listp var) (make-let-bindings-for-nested-lambda-list var `(NTH ,index ,path) push-proc macro-form macro-env) (incf index)) (t (error "~S not a variable or a list" var)))) ;; This is mapped onto &OPTIONAL parameters. (do-optional-variable (parameter) (multiple-value-bind (var initform svar) (USER::PARSE-OPTIONAL-PARAMETER parameter) (funcall push-proc `(,var (IF (<= (LENGTH ,path) ,index) ,initform (NTH ,index ,path)))) (when svar (funcall push-proc `(,svar (IF (<= (LENGTH ,path) ,index) NIL T)))) (incf index))) ;; This is mapped onto &KEY parameters. (do-key-parameter (parameter) (multiple-value-bind (var initform svar keyword) (USER::PARSE-KEY-PARAMETER parameter) (funcall push-proc `(,var (GETF (NTHCDR ,index ,path) ,keyword ,initform))) (when svar (funcall push-proc `(,svar (LABELS ((FOO (LIST) (COND ((NULL LIST) NIL) ((EQ (CAR LIST) ,keyword) T) (T (FOO (CDDR LIST)))))) (FOO (NTHCDR ,index ,path)))))))) ;; This is mapped onto &AUX parameters. (do-aux-parameter (parameter) (multiple-value-bind (var value) (USER::PARSE-AUX-PARAMETER parameter) (funcall push-proc `(,var ,value)))) ) ;; Undot the lambda list if it's dotted (when (not (null (cdr (last lambda-list)))) (setq lambda-list (rplacd (last (copy-list lambda-list)) `(&REST ,(cdr (last lambda-list)))))) ;; Parse it (multiple-value-bind (required optional rest key allow aux body whole environment) (user::moby-parse-lambda-list lambda-list) ;; Make a dummy binding that checks for ample arguments ; (let ((min-args (length required)) ; (max-args (if (or rest key aux whole) NIL (+ (length required) (length optional))))) ; (funcall push-proc `(IGNORE (REQUIRE-AMPLE-MACRO-ARGUMENTS ; ,min-args ,max-args ,path ',lambda-list (CAR ,macro-form))))) ;; Make a binding for the &WHOLE variable, if present (when whole (funcall push-proc `(,whole ,macro-form))) ;; Make a binding for the &ENVIRONMENT variable, if present (when environment (funcall push-proc `(,environment ,macro-env))) ;; Make bindings for the required parameters (mapcar #'make-binding-or-recurse required) ;; Make bindings for the &OPTIONAL parameters (mapcar #'do-optional-variable optional) ;; Make a binding for the &REST or &BODY parameter, if present (when (or rest body) (funcall push-proc `(,(if rest rest body) (NTHCDR ,index ,path)))) ;; Make bindings for the &KEY parameters (mapcar #'do-key-parameter key) ;; Ignore the &ALLOW-OTHER-KEYS business entirely allow ;; Make bindings for the &AUX parameters (mapcar #'do-aux-parameter aux))))) (defun require-ample-macro-arguments (min max arglist lambda-list macro-name) (unless (listp arglist) (error "Attempt to bind lambda list ~S in macro ~S to ~S; a list is required." lambda-list macro-name arglist)) (let ((length (length arglist))) (cond ((< length min) (error "Too few arguments to lambda list ~S in macro ~S; ~S provided, ~S expected." lambda-list macro-name length min)) ((and max (> length max)) (error "Too many arguments to lambda list ~S in macro ~S; ~S provided, ~S allowed." lambda-list macro-name length max)) (T NIL)))) ;;; GOBBLE (defun gobble-declarations (list-of-forms &optional gobble-doc-strings-too env) (let ((declarations '()) (doc-string nil) (body nil)) (loop (let ((form (MACROEXPAND (car list-of-forms) env))) (cond ((and (listp form) (eq (car form) 'declare)) (push form declarations)) ((and (stringp form) gobble-doc-strings-too (not doc-string)) (setq doc-string form)) (t (setq body list-of-forms) (return)))) (pop list-of-forms)) (when (and (null body) doc-string) (setq body (list doc-string))) (values body (reverse declarations) doc-string)))