| 743 | | (eval-when (:compile-toplevel :load-toplevel :execute) |
| 744 | | (defstruct method-combination |
| 745 | | name |
| 746 | | operator |
| 747 | | identity-with-one-argument |
| 748 | | documentation) |
| 749 | | |
| 750 | | (defun expand-short-defcombin (whole) |
| 751 | | (let* ((name (cadr whole)) |
| 752 | | (documentation |
| 753 | | (getf (cddr whole) :documentation "")) |
| 754 | | (identity-with-one-arg |
| 755 | | (getf (cddr whole) :identity-with-one-argument nil)) |
| 756 | | (operator |
| 757 | | (getf (cddr whole) :operator name))) |
| 758 | | `(progn |
| 759 | | (setf (get ',name 'method-combination-object) |
| 760 | | (make-method-combination :name ',name |
| 761 | | :operator ',operator |
| 762 | | :identity-with-one-argument ',identity-with-one-arg |
| 763 | | :documentation ',documentation)) |
| 764 | | ',name))) |
| 765 | | |
| 766 | | (defun expand-long-defcombin (whole) |
| 767 | | (declare (ignore whole)) |
| 768 | | (error "The long form of DEFINE-METHOD-COMBINATION is not implemented."))) |
| 769 | | |
| 770 | | (defmacro define-method-combination (&whole form &rest args) |
| 771 | | (declare (ignore args)) |
| | 745 | (defstruct method-combination |
| | 746 | name |
| | 747 | documentation) |
| | 748 | |
| | 749 | (defstruct (short-method-combination |
| | 750 | (:include method-combination)) |
| | 751 | operator |
| | 752 | identity-with-one-argument) |
| | 753 | |
| | 754 | (defstruct (long-method-combination |
| | 755 | (:include method-combination)) |
| | 756 | lambda-list |
| | 757 | method-group-specs |
| | 758 | args-lambda-list |
| | 759 | generic-function-symbol |
| | 760 | function |
| | 761 | arguments |
| | 762 | declarations |
| | 763 | forms) |
| | 764 | |
| | 765 | (defun expand-short-defcombin (whole) |
| | 766 | (let* ((name (cadr whole)) |
| | 767 | (documentation |
| | 768 | (getf (cddr whole) :documentation "")) |
| | 769 | (identity-with-one-arg |
| | 770 | (getf (cddr whole) :identity-with-one-argument nil)) |
| | 771 | (operator |
| | 772 | (getf (cddr whole) :operator name))) |
| | 773 | `(progn |
| | 774 | (setf (get ',name 'method-combination-object) |
| | 775 | (make-short-method-combination |
| | 776 | :name ',name |
| | 777 | :operator ',operator |
| | 778 | :identity-with-one-argument ',identity-with-one-arg |
| | 779 | :documentation ',documentation)) |
| | 780 | ',name))) |
| | 781 | |
| | 782 | ;;; |
| | 783 | ;;; long form of define-method-combination (from Sacla and XCL) |
| | 784 | ;;; |
| | 785 | (defun define-method-combination-type (name &rest initargs) |
| | 786 | (setf (get name 'method-combination-object) |
| | 787 | (apply 'make-long-method-combination initargs))) |
| | 788 | |
| | 789 | (defun method-group-p (selecter qualifiers) |
| | 790 | ;; selecter::= qualifier-pattern | predicate |
| | 791 | (etypecase selecter |
| | 792 | (list (or (equal selecter qualifiers) |
| | 793 | (let ((last (last selecter))) |
| | 794 | (when (eq '* (cdr last)) |
| | 795 | (let* ((prefix `(,@(butlast selecter) ,(car last))) |
| | 796 | (pos (mismatch prefix qualifiers))) |
| | 797 | (or (null pos) (= pos (length prefix)))))))) |
| | 798 | ((eql *) t) |
| | 799 | (symbol (funcall (symbol-function selecter) qualifiers)))) |
| | 800 | |
| | 801 | (defun check-variable-name (name) |
| | 802 | (flet ((valid-variable-name-p (name) |
| | 803 | (and (symbolp name) (not (constantp name))))) |
| | 804 | (assert (valid-variable-name-p name)))) |
| | 805 | |
| | 806 | (defun canonicalize-method-group-spec (spec) |
| | 807 | ;; spec ::= (name {qualifier-pattern+ | predicate} [[long-form-option]]) |
| | 808 | ;; long-form-option::= :description description | :order order | |
| | 809 | ;; :required required-p |
| | 810 | ;; a canonicalized-spec is a simple plist. |
| | 811 | (let* ((rest spec) |
| | 812 | (name (prog2 (check-variable-name (car rest)) |
| | 813 | (car rest) |
| | 814 | (setq rest (cdr rest)))) |
| | 815 | (option-names '(:description :order :required)) |
| | 816 | (selecters (let ((end (or (position-if #'(lambda (it) |
| | 817 | (member it option-names)) |
| | 818 | rest) |
| | 819 | (length rest)))) |
| | 820 | (prog1 (subseq rest 0 end) |
| | 821 | (setq rest (subseq rest end))))) |
| | 822 | (description (getf rest :description "")) |
| | 823 | (order (getf rest :order :most-specific-first)) |
| | 824 | (required-p (getf rest :required))) |
| | 825 | `(list :name ',name |
| | 826 | :predicate (lambda (qualifiers) |
| | 827 | (loop for item in ',selecters |
| | 828 | thereis (method-group-p item qualifiers))) |
| | 829 | :description ',description |
| | 830 | :order ,order |
| | 831 | :required ',required-p))) |
| | 832 | |
| | 833 | (defun extract-required-part (lambda-list) |
| | 834 | (flet ((skip (key lambda-list) |
| | 835 | (if (eq (first lambda-list) key) |
| | 836 | (cddr lambda-list) |
| | 837 | lambda-list))) |
| | 838 | (ldiff (skip '&environment (skip '&whole lambda-list)) |
| | 839 | (member-if #'(lambda (it) (member it lambda-list-keywords)) |
| | 840 | lambda-list)))) |
| | 841 | |
| | 842 | (defun extract-specified-part (key lambda-list) |
| | 843 | (case key |
| | 844 | ((&eval &whole) |
| | 845 | (list (second (member key lambda-list)))) |
| | 846 | (t |
| | 847 | (let ((here (cdr (member key lambda-list)))) |
| | 848 | (ldiff here |
| | 849 | (member-if #'(lambda (it) (member it lambda-list-keywords)) |
| | 850 | here)))))) |
| | 851 | |
| | 852 | (defun extract-optional-part (lambda-list) |
| | 853 | (extract-specified-part '&optional lambda-list)) |
| | 854 | |
| | 855 | (defun parse-define-method-combination-arguments-lambda-list (lambda-list) |
| | 856 | ;; Define-method-combination Arguments Lambda Lists |
| | 857 | ;; http://www.lispworks.com/reference/HyperSpec/Body/03_dj.htm |
| | 858 | (let ((required (extract-required-part lambda-list)) |
| | 859 | (whole (extract-specified-part '&whole lambda-list)) |
| | 860 | (optional (extract-specified-part '&optional lambda-list)) |
| | 861 | (rest (extract-specified-part '&rest lambda-list)) |
| | 862 | (keys (extract-specified-part '&key lambda-list)) |
| | 863 | (aux (extract-specified-part '&aux lambda-list))) |
| | 864 | (values (first whole) |
| | 865 | required |
| | 866 | (mapcar #'(lambda (spec) |
| | 867 | (if (consp spec) |
| | 868 | `(,(first spec) ,(second spec) ,@(cddr spec)) |
| | 869 | `(,spec nil))) |
| | 870 | optional) |
| | 871 | (first rest) |
| | 872 | (mapcar #'(lambda (spec) |
| | 873 | (let ((key (if (consp spec) (car spec) spec)) |
| | 874 | (rest (when (consp spec) (rest spec)))) |
| | 875 | `(,(if (consp key) key `(,(make-keyword key) ,key)) |
| | 876 | ,(car rest) |
| | 877 | ,@(cdr rest)))) |
| | 878 | keys) |
| | 879 | (mapcar #'(lambda (spec) |
| | 880 | (if (consp spec) |
| | 881 | `(,(first spec) ,(second spec)) |
| | 882 | `(,spec nil))) |
| | 883 | aux)))) |
| | 884 | |
| | 885 | (defmacro getk (plist key init-form) |
| | 886 | "Similar to getf except eval and return INIT-FORM if KEY has no value in PLIST." |
| | 887 | (let ((not-exist (gensym)) |
| | 888 | (value (gensym))) |
| | 889 | `(let ((,value (getf ,plist ,key ,not-exist))) |
| | 890 | (if (eq ,not-exist ,value) ,init-form ,value)))) |
| | 891 | |
| | 892 | (defconstant +gf-args-var+ (make-symbol "GF-ARGS-VAR")) |
| | 893 | |
| | 894 | (defmacro with-args-lambda-list (args-lambda-list generic-function-symbol |
| | 895 | &body forms) |
| | 896 | (let ((gf-lambda-list (gensym)) |
| | 897 | (nrequired (gensym)) |
| | 898 | (noptional (gensym)) |
| | 899 | (rest-args (gensym))) |
| | 900 | (multiple-value-bind (whole required optional rest keys aux) |
| | 901 | (parse-define-method-combination-arguments-lambda-list args-lambda-list) |
| | 902 | `(let* ((,gf-lambda-list (slot-value ,generic-function-symbol 'lambda-list)) |
| | 903 | (,nrequired (length (extract-required-part ,gf-lambda-list))) |
| | 904 | (,noptional (length (extract-optional-part ,gf-lambda-list))) |
| | 905 | (,rest-args (subseq ,+gf-args-var+ (+ ,nrequired ,noptional))) |
| | 906 | ,@(when whole `((,whole ,+gf-args-var+))) |
| | 907 | ,@(loop for var in required and i upfrom 0 |
| | 908 | collect `(,var (when (< ,i ,nrequired) |
| | 909 | (nth ,i ,+gf-args-var+)))) |
| | 910 | ,@(loop for (var init-form) in optional and i upfrom 0 |
| | 911 | collect |
| | 912 | `(,var (if (< ,i ,noptional) |
| | 913 | (nth (+ ,nrequired ,i) ,+gf-args-var+) |
| | 914 | ,init-form))) |
| | 915 | ,@(when rest `((,rest ,rest-args))) |
| | 916 | ,@(loop for ((key var) init-form) in keys and i upfrom 0 |
| | 917 | collect `(,var (getk ,rest-args ',key ,init-form))) |
| | 918 | ,@(loop for (var init-form) in aux and i upfrom 0 |
| | 919 | collect `(,var ,init-form))) |
| | 920 | ,@forms)))) |
| | 921 | |
| | 922 | (defmacro with-method-groups (method-group-specs methods-form &body forms) |
| | 923 | (flet ((grouping-form (spec methods-var) |
| | 924 | (let ((predicate (coerce-to-function (getf spec :predicate))) |
| | 925 | (group (gensym)) |
| | 926 | (leftovers (gensym)) |
| | 927 | (method (gensym))) |
| | 928 | `(let ((,group '()) |
| | 929 | (,leftovers '())) |
| | 930 | (dolist (,method ,methods-var) |
| | 931 | (if (funcall ,predicate (method-qualifiers ,method)) |
| | 932 | (push ,method ,group) |
| | 933 | (push ,method ,leftovers))) |
| | 934 | (ecase ,(getf spec :order) |
| | 935 | (:most-specific-last ) |
| | 936 | (:most-specific-first (setq ,group (nreverse ,group)))) |
| | 937 | ,@(when (getf spec :required) |
| | 938 | `((when (null ,group) |
| | 939 | (error "Method group ~S must not be empty." |
| | 940 | ',(getf spec :name))))) |
| | 941 | (setq ,methods-var (nreverse ,leftovers)) |
| | 942 | ,group)))) |
| | 943 | (let ((rest (gensym)) |
| | 944 | (method (gensym))) |
| | 945 | `(let* ((,rest ,methods-form) |
| | 946 | ,@(mapcar #'(lambda (spec) |
| | 947 | `(,(getf spec :name) ,(grouping-form spec rest))) |
| | 948 | method-group-specs)) |
| | 949 | (dolist (,method ,rest) |
| | 950 | (invalid-method-error ,method |
| | 951 | "Method ~S with qualifiers ~S does not belong to any method group." |
| | 952 | ,method (method-qualifiers ,method))) |
| | 953 | ,@forms)))) |
| | 954 | |
| | 955 | (defun method-combination-type-lambda |
| | 956 | (&key name lambda-list args-lambda-list generic-function-symbol |
| | 957 | method-group-specs declarations forms &allow-other-keys) |
| | 958 | (let ((methods (gensym))) |
| | 959 | `(lambda (,generic-function-symbol ,methods ,@lambda-list) |
| | 960 | ,@declarations |
| | 961 | (let ((*message-prefix* ,(format nil "METHOD COMBINATION TYPE ~S: " name))) |
| | 962 | (with-method-groups ,method-group-specs |
| | 963 | ,methods |
| | 964 | ,@(if (null args-lambda-list) |
| | 965 | forms |
| | 966 | `((with-args-lambda-list ,args-lambda-list |
| | 967 | ,generic-function-symbol |
| | 968 | ,@forms)))))))) |
| | 969 | |
| | 970 | (defun declarationp (expr) |
| | 971 | (and (consp expr) (eq (car expr) 'DECLARE))) |
| | 972 | |
| | 973 | (defun long-form-method-combination-args (args) |
| | 974 | ;; define-method-combination name lambda-list (method-group-specifier*) args |
| | 975 | ;; args ::= [(:arguments . args-lambda-list)] |
| | 976 | ;; [(:generic-function generic-function-symbol)] |
| | 977 | ;; [[declaration* | documentation]] form* |
| | 978 | (let ((rest args)) |
| | 979 | (labels ((nextp (key) (and (consp (car rest)) (eq key (caar rest)))) |
| | 980 | (args-lambda-list () |
| | 981 | (when (nextp :arguments) |
| | 982 | (prog1 (cdr (car rest)) (setq rest (cdr rest))))) |
| | 983 | (generic-function-symbol () |
| | 984 | (if (nextp :generic-function) |
| | 985 | (prog1 (second (car rest)) (setq rest (cdr rest))) |
| | 986 | (gensym))) |
| | 987 | (declaration* () |
| | 988 | (let ((end (position-if-not #'declarationp rest))) |
| | 989 | (when end |
| | 990 | (prog1 (subseq rest 0 end) (setq rest (nthcdr end rest)))))) |
| | 991 | (documentation? () |
| | 992 | (when (stringp (car rest)) |
| | 993 | (prog1 (car rest) (setq rest (cdr rest))))) |
| | 994 | (form* () rest)) |
| | 995 | (let ((declarations '())) |
| | 996 | `(:args-lambda-list ,(args-lambda-list) |
| | 997 | :generic-function-symbol ,(generic-function-symbol) |
| | 998 | :documentation ,(prog2 (setq declarations (declaration*)) |
| | 999 | (documentation?)) |
| | 1000 | :declarations (,@declarations ,@(declaration*)) |
| | 1001 | :forms ,(form*)))))) |
| | 1002 | |
| | 1003 | (defun define-long-form-method-combination (name lambda-list method-group-specs |
| | 1004 | &rest args) |
| | 1005 | (let* ((initargs `(:name ,name |
| | 1006 | :lambda-list ,lambda-list |
| | 1007 | :method-group-specs ,method-group-specs |
| | 1008 | ,@(long-form-method-combination-args args))) |
| | 1009 | (lambda-expression (apply #'method-combination-type-lambda initargs))) |
| | 1010 | (apply #'define-method-combination-type name |
| | 1011 | `(,@initargs |
| | 1012 | ;; :function ,(compile nil lambda-expression) |
| | 1013 | :function ,(coerce-to-function lambda-expression))) |
| | 1014 | name)) |
| | 1015 | |
| | 1016 | (defun expand-long-defcombin (name args) |
| | 1017 | (destructuring-bind (lambda-list method-groups &rest body) args |
| | 1018 | `(apply #'define-long-form-method-combination |
| | 1019 | ',name |
| | 1020 | ',lambda-list |
| | 1021 | (list ,@(mapcar #'canonicalize-method-group-spec method-groups)) |
| | 1022 | ',body))) |
| | 1023 | |
| | 1024 | (defmacro define-method-combination (&whole form name &rest args) |