diff options
| author | Daniel Colascione | 2014-04-19 19:34:22 -0700 |
|---|---|---|
| committer | Daniel Colascione | 2014-04-19 19:34:22 -0700 |
| commit | 89a2e783c2f22b4932dd77c16a0e357c5c17a4bf (patch) | |
| tree | e3cdb05ac00ce099145ac0205c6ce12da43e7f1d | |
| parent | 6dfa19c50f75c1892f5c9a48104ddd532796d089 (diff) | |
| download | emacs-89a2e783c2f22b4932dd77c16a0e357c5c17a4bf.tar.gz emacs-89a2e783c2f22b4932dd77c16a0e357c5c17a4bf.zip | |
defstruct introspection
| -rw-r--r-- | doc/misc/cl.texi | 40 | ||||
| -rw-r--r-- | etc/ChangeLog | 4 | ||||
| -rw-r--r-- | etc/NEWS | 3 | ||||
| -rw-r--r-- | lisp/ChangeLog | 13 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-macs.el | 119 | ||||
| -rw-r--r-- | test/ChangeLog | 4 | ||||
| -rw-r--r-- | test/automated/cl-lib.el | 19 |
7 files changed, 192 insertions, 10 deletions
diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi index f0ac289acab..1c202961889 100644 --- a/doc/misc/cl.texi +++ b/doc/misc/cl.texi | |||
| @@ -4247,6 +4247,46 @@ of the included type and the first new slot. | |||
| 4247 | Except as noted, the @code{cl-defstruct} facility of this package is | 4247 | Except as noted, the @code{cl-defstruct} facility of this package is |
| 4248 | entirely compatible with that of Common Lisp. | 4248 | entirely compatible with that of Common Lisp. |
| 4249 | 4249 | ||
| 4250 | The @code{cl-defstruct} package also provides a few structure | ||
| 4251 | introspection functions. | ||
| 4252 | |||
| 4253 | @defun cl-struct-sequence-type struct-type | ||
| 4254 | This function returns the underlying data structure for | ||
| 4255 | @code{struct-type}, which is a symbol. It returns @code{vector} or | ||
| 4256 | @code{list}, or @code{nil} if @code{struct-type} is not actually a | ||
| 4257 | structure. | ||
| 4258 | |||
| 4259 | @defun cl-struct-slot-info struct-type | ||
| 4260 | This function returns a list of slot descriptors for structure | ||
| 4261 | @code{struct-type}. Each entry in the list is @code{(name . opts)}, | ||
| 4262 | where @code{name} is the name of the slot and @code{opts} is the list | ||
| 4263 | of slot options given to @code{defstruct}. Dummy entries represent | ||
| 4264 | the slots used for the struct name and that are skipped to implement | ||
| 4265 | @code{:initial-offset}. | ||
| 4266 | |||
| 4267 | @defun cl-struct-slot-offset struct-type slot-name | ||
| 4268 | Return the offset of slot @code{slot-name} in @code{struct-type}. The | ||
| 4269 | returned zero-based slot index is relative to the start of the | ||
| 4270 | structure data type and is adjusted for any structure name and | ||
| 4271 | :initial-offset slots. Signal error if struct @code{struct-type} does | ||
| 4272 | not contain @code{slot-name}. | ||
| 4273 | |||
| 4274 | @defun cl-struct-slot-value struct-type slot-name inst | ||
| 4275 | Return the value of slot @code{slot-name} in @code{inst} of | ||
| 4276 | @code{struct-type}. @code{struct} and @code{slot-name} are symbols. | ||
| 4277 | @code{inst} is a structure instance. This routine is also a | ||
| 4278 | @code{setf} place. @code{cl-struct-slot-value} uses | ||
| 4279 | @code{cl-struct-slot-offset} internally and can signal the same | ||
| 4280 | errors. | ||
| 4281 | |||
| 4282 | @defun cl-struct-set-slot-value struct-type slot-name inst value | ||
| 4283 | Set the value of slot @code{slot-name} in @code{inst} of | ||
| 4284 | @code{struct-type}. @code{struct} and @code{slot-name} are symbols. | ||
| 4285 | @code{inst} is a structure instance. @code{value} is the value to | ||
| 4286 | which to set the given slot. Return @code{value}. | ||
| 4287 | @code{cl-struct-slot-value} uses @code{cl-struct-set-slot-offset} | ||
| 4288 | internally and can signal the same errors. | ||
| 4289 | |||
| 4250 | @node Assertions | 4290 | @node Assertions |
| 4251 | @chapter Assertions and Errors | 4291 | @chapter Assertions and Errors |
| 4252 | 4292 | ||
diff --git a/etc/ChangeLog b/etc/ChangeLog index 1672b0f06ac..de57d81a685 100644 --- a/etc/ChangeLog +++ b/etc/ChangeLog | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | 2014-04-20 Daniel Colascione <dancol@dancol.org> | ||
| 2 | |||
| 3 | * NEWS: Mention new struct functions. | ||
| 4 | |||
| 1 | 2014-04-17 Daniel Colascione <dancol@dancol.org> | 5 | 2014-04-17 Daniel Colascione <dancol@dancol.org> |
| 2 | 6 | ||
| 3 | * NEWS: Mention bracketed paste support. | 7 | * NEWS: Mention bracketed paste support. |
| @@ -97,6 +97,9 @@ active region handling. | |||
| 97 | ** You can specify a function's interactive-only property via `declare'. | 97 | ** You can specify a function's interactive-only property via `declare'. |
| 98 | However you specify it, the property affects `describe-function' output. | 98 | However you specify it, the property affects `describe-function' output. |
| 99 | 99 | ||
| 100 | ** You can access the slots of structures using `cl-struct-slot-value' | ||
| 101 | and `cl-struct-set-slot-value'. | ||
| 102 | |||
| 100 | 103 | ||
| 101 | * Changes in Emacs 24.5 on Non-Free Operating Systems | 104 | * Changes in Emacs 24.5 on Non-Free Operating Systems |
| 102 | 105 | ||
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 648e5f3869e..a1da41a5695 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,16 @@ | |||
| 1 | 2014-04-20 Daniel Colascione <dancol@dancol.org> | ||
| 2 | |||
| 3 | * emacs-lisp/cl-macs.el (cl-the): Make `cl-the' assert its type | ||
| 4 | argument. | ||
| 5 | (cl--const-expr-val): cl--const-expr-val should macroexpand its | ||
| 6 | argument in case we're inside a symbol-macrolet. | ||
| 7 | (cl--do-arglist, cl--compiler-macro-typep) | ||
| 8 | (cl--compiler-macro-member, cl--compiler-macro-assoc): Pass macro | ||
| 9 | environment to `cl--const-expr-val'. | ||
| 10 | (cl-struct-sequence-type,cl-struct-slot-info) | ||
| 11 | (cl-struct-slot-offset, cl-struct-slot-value) | ||
| 12 | (cl-struct-set-slot-value): New functions. | ||
| 13 | |||
| 1 | 2014-04-19 Stefan Monnier <monnier@iro.umontreal.ca> | 14 | 2014-04-19 Stefan Monnier <monnier@iro.umontreal.ca> |
| 2 | 15 | ||
| 3 | * progmodes/sh-script.el (sh-smie--sh-keyword-p): Handle variable | 16 | * progmodes/sh-script.el (sh-smie--sh-keyword-p): Handle variable |
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index cd2d52a4b21..b0a5c442d46 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el | |||
| @@ -134,8 +134,15 @@ | |||
| 134 | ((symbolp x) (and (memq x '(nil t)) t)) | 134 | ((symbolp x) (and (memq x '(nil t)) t)) |
| 135 | (t t))) | 135 | (t t))) |
| 136 | 136 | ||
| 137 | (defun cl--const-expr-val (x) | 137 | (defun cl--const-expr-val (x &optional environment default) |
| 138 | (and (macroexp-const-p x) (if (consp x) (nth 1 x) x))) | 138 | "Return the value of X known at compile-time. |
| 139 | If X is not known at compile time, return DEFAULT. Before | ||
| 140 | testing whether X is known at compile time, macroexpand it in | ||
| 141 | ENVIRONMENT." | ||
| 142 | (let ((x (macroexpand-all x environment))) | ||
| 143 | (if (macroexp-const-p x) | ||
| 144 | (if (consp x) (nth 1 x) x) | ||
| 145 | default))) | ||
| 139 | 146 | ||
| 140 | (defun cl--expr-contains (x y) | 147 | (defun cl--expr-contains (x y) |
| 141 | "Count number of times X refers to Y. Return nil for 0 times." | 148 | "Count number of times X refers to Y. Return nil for 0 times." |
| @@ -519,7 +526,8 @@ its argument list allows full Common Lisp conventions." | |||
| 519 | look | 526 | look |
| 520 | `(or ,look | 527 | `(or ,look |
| 521 | ,(if (eq (cl--const-expr-p def) t) | 528 | ,(if (eq (cl--const-expr-p def) t) |
| 522 | `'(nil ,(cl--const-expr-val def)) | 529 | `'(nil ,(cl--const-expr-val |
| 530 | def macroexpand-all-environment)) | ||
| 523 | `(list nil ,def)))))))) | 531 | `(list nil ,def)))))))) |
| 524 | (push karg keys))))) | 532 | (push karg keys))))) |
| 525 | (setq keys (nreverse keys)) | 533 | (setq keys (nreverse keys)) |
| @@ -2057,10 +2065,21 @@ values. For compatibility, (cl-values A B C) is a synonym for (list A B C). | |||
| 2057 | (declare (debug t)) | 2065 | (declare (debug t)) |
| 2058 | (cons 'progn body)) | 2066 | (cons 'progn body)) |
| 2059 | ;;;###autoload | 2067 | ;;;###autoload |
| 2060 | (defmacro cl-the (_type form) | 2068 | (defmacro cl-the (type form) |
| 2061 | "At present this ignores TYPE and is simply equivalent to FORM." | 2069 | "Return FORM. If type-checking is enabled, assert that it is of TYPE." |
| 2062 | (declare (indent 1) (debug (cl-type-spec form))) | 2070 | (declare (indent 1) (debug (cl-type-spec form))) |
| 2063 | form) | 2071 | (if (not (or (not (cl--compiling-file)) |
| 2072 | (< cl--optimize-speed 3) | ||
| 2073 | (= cl--optimize-safety 3))) | ||
| 2074 | form | ||
| 2075 | (let* ((temp (if (cl--simple-expr-p form 3) | ||
| 2076 | form (make-symbol "--cl-var--"))) | ||
| 2077 | (body `(progn (unless ,(cl--make-type-test temp type) | ||
| 2078 | (signal 'wrong-type-argument | ||
| 2079 | (list ',type ,temp ',form))) | ||
| 2080 | ,temp))) | ||
| 2081 | (if (eq temp form) body | ||
| 2082 | `(let ((,temp ,form)) ,body))))) | ||
| 2064 | 2083 | ||
| 2065 | (defvar cl--proclaim-history t) ; for future compilers | 2084 | (defvar cl--proclaim-history t) ; for future compilers |
| 2066 | (defvar cl--declare-stack t) ; for future compilers | 2085 | (defvar cl--declare-stack t) ; for future compilers |
| @@ -2577,6 +2596,83 @@ non-nil value, that slot cannot be set via `setf'. | |||
| 2577 | forms) | 2596 | forms) |
| 2578 | `(progn ,@(nreverse (cons `',name forms))))) | 2597 | `(progn ,@(nreverse (cons `',name forms))))) |
| 2579 | 2598 | ||
| 2599 | (defun cl-struct-sequence-type (struct-type) | ||
| 2600 | "Return the sequence used to build STRUCT-TYPE. | ||
| 2601 | STRUCT-TYPE is a symbol naming a struct type. Return 'vector or | ||
| 2602 | 'list, or nil if STRUCT-TYPE is not a struct type. " | ||
| 2603 | (car (get struct-type 'cl-struct-type))) | ||
| 2604 | (put 'cl-struct-sequence-type 'side-effect-free t) | ||
| 2605 | |||
| 2606 | (defun cl-struct-slot-info (struct-type) | ||
| 2607 | "Return a list of slot names of struct STRUCT-TYPE. | ||
| 2608 | Each entry is a list (SLOT-NAME . OPTS), where SLOT-NAME is a | ||
| 2609 | slot name symbol and OPTS is a list of slot options given to | ||
| 2610 | `cl-defstruct'. Dummy slots that represent the struct name and | ||
| 2611 | slots skipped by :initial-offset may appear in the list." | ||
| 2612 | (get struct-type 'cl-struct-slots)) | ||
| 2613 | (put 'cl-struct-slot-info 'side-effect-free t) | ||
| 2614 | |||
| 2615 | (defun cl-struct-slot-offset (struct-type slot-name) | ||
| 2616 | "Return the offset of slot SLOT-NAME in STRUCT-TYPE. | ||
| 2617 | The returned zero-based slot index is relative to the start of | ||
| 2618 | the structure data type and is adjusted for any structure name | ||
| 2619 | and :initial-offset slots. Signal error if struct STRUCT-TYPE | ||
| 2620 | does not contain SLOT-NAME." | ||
| 2621 | (or (cl-position slot-name | ||
| 2622 | (cl-struct-slot-info struct-type) | ||
| 2623 | :key #'car :test #'eq) | ||
| 2624 | (error "struct %s has no slot %s" struct-type slot-name))) | ||
| 2625 | (put 'cl-struct-slot-offset 'side-effect-free t) | ||
| 2626 | |||
| 2627 | (defun cl-struct-slot-value (struct-type slot-name inst) | ||
| 2628 | "Return the value of slot SLOT-NAME in INST of STRUCT-TYPE. | ||
| 2629 | STRUCT and SLOT-NAME are symbols. INST is a structure instance." | ||
| 2630 | (unless (cl-typep inst struct-type) | ||
| 2631 | (signal 'wrong-type-argument (list struct-type inst))) | ||
| 2632 | (elt inst (cl-struct-slot-offset struct-type slot-name))) | ||
| 2633 | (put 'cl-struct-slot-value 'side-effect-free t) | ||
| 2634 | |||
| 2635 | (defun cl-struct-set-slot-value (struct-type slot-name inst value) | ||
| 2636 | "Set the value of slot SLOT-NAME in INST of STRUCT-TYPE. | ||
| 2637 | STRUCT and SLOT-NAME are symbols. INST is a structure instance. | ||
| 2638 | VALUE is the value to which to set the given slot. Return | ||
| 2639 | VALUE." | ||
| 2640 | (unless (cl-typep inst struct-type) | ||
| 2641 | (signal 'wrong-type-argument (list struct-type inst))) | ||
| 2642 | (setf (elt inst (cl-struct-slot-offset struct-type slot-name)) value)) | ||
| 2643 | |||
| 2644 | (defsetf cl-struct-slot-value cl-struct-set-slot-value) | ||
| 2645 | |||
| 2646 | (cl-define-compiler-macro cl-struct-slot-value | ||
| 2647 | (&whole orig struct-type slot-name inst) | ||
| 2648 | (or (let* ((macenv macroexpand-all-environment) | ||
| 2649 | (struct-type (cl--const-expr-val struct-type macenv)) | ||
| 2650 | (slot-name (cl--const-expr-val slot-name macenv))) | ||
| 2651 | (and struct-type (symbolp struct-type) | ||
| 2652 | slot-name (symbolp slot-name) | ||
| 2653 | (assq slot-name (cl-struct-slot-info struct-type)) | ||
| 2654 | (let ((idx (cl-struct-slot-offset struct-type slot-name))) | ||
| 2655 | (cl-ecase (cl-struct-sequence-type struct-type) | ||
| 2656 | (vector `(aref (cl-the ,struct-type ,inst) ,idx)) | ||
| 2657 | (list `(nth ,idx (cl-the ,struct-type ,inst))))))) | ||
| 2658 | orig)) | ||
| 2659 | |||
| 2660 | (cl-define-compiler-macro cl-struct-set-slot-value | ||
| 2661 | (&whole orig struct-type slot-name inst value) | ||
| 2662 | (or (let* ((macenv macroexpand-all-environment) | ||
| 2663 | (struct-type (cl--const-expr-val struct-type macenv)) | ||
| 2664 | (slot-name (cl--const-expr-val slot-name macenv))) | ||
| 2665 | (and struct-type (symbolp struct-type) | ||
| 2666 | slot-name (symbolp slot-name) | ||
| 2667 | (assq slot-name (cl-struct-slot-info struct-type)) | ||
| 2668 | (let ((idx (cl-struct-slot-offset struct-type slot-name))) | ||
| 2669 | (cl-ecase (cl-struct-sequence-type struct-type) | ||
| 2670 | (vector `(setf (aref (cl-the ,struct-type ,inst) ,idx) | ||
| 2671 | ,value)) | ||
| 2672 | (list `(setf (nth ,idx (cl-the ,struct-type ,inst)) | ||
| 2673 | ,value)))))) | ||
| 2674 | orig)) | ||
| 2675 | |||
| 2580 | ;;; Types and assertions. | 2676 | ;;; Types and assertions. |
| 2581 | 2677 | ||
| 2582 | ;;;###autoload | 2678 | ;;;###autoload |
| @@ -2653,7 +2749,8 @@ TYPE is a Common Lisp-style type specifier." | |||
| 2653 | (defun cl--compiler-macro-typep (form val type) | 2749 | (defun cl--compiler-macro-typep (form val type) |
| 2654 | (if (macroexp-const-p type) | 2750 | (if (macroexp-const-p type) |
| 2655 | (macroexp-let2 macroexp-copyable-p temp val | 2751 | (macroexp-let2 macroexp-copyable-p temp val |
| 2656 | (cl--make-type-test temp (cl--const-expr-val type))) | 2752 | (cl--make-type-test temp (cl--const-expr-val |
| 2753 | type macroexpand-all-environment))) | ||
| 2657 | form)) | 2754 | form)) |
| 2658 | 2755 | ||
| 2659 | ;;;###autoload | 2756 | ;;;###autoload |
| @@ -2829,7 +2926,8 @@ The function's arguments should be treated as immutable. | |||
| 2829 | 2926 | ||
| 2830 | (defun cl--compiler-macro-member (form a list &rest keys) | 2927 | (defun cl--compiler-macro-member (form a list &rest keys) |
| 2831 | (let ((test (and (= (length keys) 2) (eq (car keys) :test) | 2928 | (let ((test (and (= (length keys) 2) (eq (car keys) :test) |
| 2832 | (cl--const-expr-val (nth 1 keys))))) | 2929 | (cl--const-expr-val (nth 1 keys) |
| 2930 | macroexpand-all-environment)))) | ||
| 2833 | (cond ((eq test 'eq) `(memq ,a ,list)) | 2931 | (cond ((eq test 'eq) `(memq ,a ,list)) |
| 2834 | ((eq test 'equal) `(member ,a ,list)) | 2932 | ((eq test 'equal) `(member ,a ,list)) |
| 2835 | ((or (null keys) (eq test 'eql)) `(memql ,a ,list)) | 2933 | ((or (null keys) (eq test 'eql)) `(memql ,a ,list)) |
| @@ -2837,11 +2935,12 @@ The function's arguments should be treated as immutable. | |||
| 2837 | 2935 | ||
| 2838 | (defun cl--compiler-macro-assoc (form a list &rest keys) | 2936 | (defun cl--compiler-macro-assoc (form a list &rest keys) |
| 2839 | (let ((test (and (= (length keys) 2) (eq (car keys) :test) | 2937 | (let ((test (and (= (length keys) 2) (eq (car keys) :test) |
| 2840 | (cl--const-expr-val (nth 1 keys))))) | 2938 | (cl--const-expr-val (nth 1 keys) |
| 2939 | macroexpand-all-environment)))) | ||
| 2841 | (cond ((eq test 'eq) `(assq ,a ,list)) | 2940 | (cond ((eq test 'eq) `(assq ,a ,list)) |
| 2842 | ((eq test 'equal) `(assoc ,a ,list)) | 2941 | ((eq test 'equal) `(assoc ,a ,list)) |
| 2843 | ((and (macroexp-const-p a) (or (null keys) (eq test 'eql))) | 2942 | ((and (macroexp-const-p a) (or (null keys) (eq test 'eql))) |
| 2844 | (if (floatp (cl--const-expr-val a)) | 2943 | (if (floatp (cl--const-expr-val a macroexpand-all-environment)) |
| 2845 | `(assoc ,a ,list) `(assq ,a ,list))) | 2944 | `(assoc ,a ,list) `(assq ,a ,list))) |
| 2846 | (t form)))) | 2945 | (t form)))) |
| 2847 | 2946 | ||
diff --git a/test/ChangeLog b/test/ChangeLog index 338a825f51e..940ed0b0b91 100644 --- a/test/ChangeLog +++ b/test/ChangeLog | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | 2014-04-20 Daniel Colascione <dancol@dancol.org> | ||
| 2 | |||
| 3 | * automated/cl-lib.el (cl-lib-struct-accessors,cl-the): New tests. | ||
| 4 | |||
| 1 | 2014-04-19 Michael Albinus <michael.albinus@gmx.de> | 5 | 2014-04-19 Michael Albinus <michael.albinus@gmx.de> |
| 2 | 6 | ||
| 3 | * automated/tramp-tests.el (tramp--test-check-files): Extend test. | 7 | * automated/tramp-tests.el (tramp--test-check-files): Extend test. |
diff --git a/test/automated/cl-lib.el b/test/automated/cl-lib.el index f7f4314e1cb..a0df07e54ea 100644 --- a/test/automated/cl-lib.el +++ b/test/automated/cl-lib.el | |||
| @@ -201,4 +201,23 @@ | |||
| 201 | :b :a :a 42) | 201 | :b :a :a 42) |
| 202 | '(42 :a)))) | 202 | '(42 :a)))) |
| 203 | 203 | ||
| 204 | (ert-deftest cl-lib-struct-accessors () | ||
| 205 | (cl-defstruct mystruct (abc :readonly t) def) | ||
| 206 | (let ((x (make-mystruct :abc 1 :def 2))) | ||
| 207 | (should (eql (cl-struct-slot-value 'mystruct 'abc x) 1)) | ||
| 208 | (should (eql (cl-struct-slot-value 'mystruct 'def x) 2)) | ||
| 209 | (cl-struct-set-slot-value 'mystruct 'def x -1) | ||
| 210 | (should (eql (cl-struct-slot-value 'mystruct 'def x) -1)) | ||
| 211 | (should (eql (cl-struct-slot-offset 'mystruct 'abc) 1)) | ||
| 212 | (should-error (cl-struct-slot-offset 'mystruct 'marypoppins)) | ||
| 213 | (should (equal (cl-struct-slot-info 'mystruct) | ||
| 214 | '((cl-tag-slot) (abc :readonly t) (def)))))) | ||
| 215 | |||
| 216 | (ert-deftest cl-the () | ||
| 217 | (should (eql (the integer 42) 42)) | ||
| 218 | (should-error (the integer "abc")) | ||
| 219 | (let ((sideffect 0)) | ||
| 220 | (should (= (the integer (incf sideffect)) 1)) | ||
| 221 | (should (= sideffect 1)))) | ||
| 222 | |||
| 204 | ;;; cl-lib.el ends here | 223 | ;;; cl-lib.el ends here |