diff options
| author | Mattias Engdegård | 2024-03-10 13:18:22 +0100 |
|---|---|---|
| committer | Mattias Engdegård | 2024-03-29 11:39:38 +0100 |
| commit | 1232ab31c656b8564984a758957466f90ac10501 (patch) | |
| tree | 38a7774207a5ac8dba2612bef9a6a39f3cd0d658 /src | |
| parent | c3684b97885c5a1f4d0713ff45c7395e9a4c6e8a (diff) | |
| download | emacs-1232ab31c656b8564984a758957466f90ac10501.tar.gz emacs-1232ab31c656b8564984a758957466f90ac10501.zip | |
Add `value<` (bug#69709)
It's a general-purpose polymorphic ordering function, like `<` but
for any two values of the same type.
* src/data.c (syms_of_data): Add the `type-mismatch` error.
(bits_word_to_host_endian): Move...
* src/lisp.h (bits_word_to_host_endian): ...here, and declare inline.
* src/fns.c (Fstring_lessp): Extract the bulk of this function to...
(string_cmp): ...this 3-way comparison function, for use elsewhere.
(bool_vector_cmp, value_cmp, Fvaluelt): New.
* lisp/emacs-lisp/byte-opt.el (side-effect-free-fns, pure-fns):
Add `value<`, which is pure and side-effect-free.
* test/src/fns-tests.el (fns-value<-ordered, fns-value<-unordered)
(fns-value<-type-mismatch, fns-value<-symbol-with-pos)
(fns-value<-circle, ert-deftest fns-value<-bool-vector): New tests.
* doc/lispref/sequences.texi (Sequence Functions):
* doc/lispref/numbers.texi (Comparison of Numbers):
* doc/lispref/strings.texi (Text Comparison):
Document the new value< function.
* etc/NEWS: Announce.
Diffstat (limited to 'src')
| -rw-r--r-- | src/data.c | 26 | ||||
| -rw-r--r-- | src/fns.c | 280 | ||||
| -rw-r--r-- | src/lisp.h | 24 |
3 files changed, 285 insertions, 45 deletions
diff --git a/src/data.c b/src/data.c index 69b990bed76..a86f86c52f5 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -3835,30 +3835,6 @@ count_trailing_zero_bits (bits_word val) | |||
| 3835 | } | 3835 | } |
| 3836 | } | 3836 | } |
| 3837 | 3837 | ||
| 3838 | static bits_word | ||
| 3839 | bits_word_to_host_endian (bits_word val) | ||
| 3840 | { | ||
| 3841 | #ifndef WORDS_BIGENDIAN | ||
| 3842 | return val; | ||
| 3843 | #else | ||
| 3844 | if (BITS_WORD_MAX >> 31 == 1) | ||
| 3845 | return bswap_32 (val); | ||
| 3846 | if (BITS_WORD_MAX >> 31 >> 31 >> 1 == 1) | ||
| 3847 | return bswap_64 (val); | ||
| 3848 | { | ||
| 3849 | int i; | ||
| 3850 | bits_word r = 0; | ||
| 3851 | for (i = 0; i < sizeof val; i++) | ||
| 3852 | { | ||
| 3853 | r = ((r << 1 << (CHAR_BIT - 1)) | ||
| 3854 | | (val & ((1u << 1 << (CHAR_BIT - 1)) - 1))); | ||
| 3855 | val = val >> 1 >> (CHAR_BIT - 1); | ||
| 3856 | } | ||
| 3857 | return r; | ||
| 3858 | } | ||
| 3859 | #endif | ||
| 3860 | } | ||
| 3861 | |||
| 3862 | DEFUN ("bool-vector-exclusive-or", Fbool_vector_exclusive_or, | 3838 | DEFUN ("bool-vector-exclusive-or", Fbool_vector_exclusive_or, |
| 3863 | Sbool_vector_exclusive_or, 2, 3, 0, | 3839 | Sbool_vector_exclusive_or, 2, 3, 0, |
| 3864 | doc: /* Return A ^ B, bitwise exclusive or. | 3840 | doc: /* Return A ^ B, bitwise exclusive or. |
| @@ -4072,6 +4048,7 @@ syms_of_data (void) | |||
| 4072 | DEFSYM (Qminibuffer_quit, "minibuffer-quit"); | 4048 | DEFSYM (Qminibuffer_quit, "minibuffer-quit"); |
| 4073 | DEFSYM (Qwrong_length_argument, "wrong-length-argument"); | 4049 | DEFSYM (Qwrong_length_argument, "wrong-length-argument"); |
| 4074 | DEFSYM (Qwrong_type_argument, "wrong-type-argument"); | 4050 | DEFSYM (Qwrong_type_argument, "wrong-type-argument"); |
| 4051 | DEFSYM (Qtype_mismatch, "type-mismatch") | ||
| 4075 | DEFSYM (Qargs_out_of_range, "args-out-of-range"); | 4052 | DEFSYM (Qargs_out_of_range, "args-out-of-range"); |
| 4076 | DEFSYM (Qvoid_function, "void-function"); | 4053 | DEFSYM (Qvoid_function, "void-function"); |
| 4077 | DEFSYM (Qcyclic_function_indirection, "cyclic-function-indirection"); | 4054 | DEFSYM (Qcyclic_function_indirection, "cyclic-function-indirection"); |
| @@ -4163,6 +4140,7 @@ syms_of_data (void) | |||
| 4163 | PUT_ERROR (Quser_error, error_tail, ""); | 4140 | PUT_ERROR (Quser_error, error_tail, ""); |
| 4164 | PUT_ERROR (Qwrong_length_argument, error_tail, "Wrong length argument"); | 4141 | PUT_ERROR (Qwrong_length_argument, error_tail, "Wrong length argument"); |
| 4165 | PUT_ERROR (Qwrong_type_argument, error_tail, "Wrong type argument"); | 4142 | PUT_ERROR (Qwrong_type_argument, error_tail, "Wrong type argument"); |
| 4143 | PUT_ERROR (Qtype_mismatch, error_tail, "Types do not match"); | ||
| 4166 | PUT_ERROR (Qargs_out_of_range, error_tail, "Args out of range"); | 4144 | PUT_ERROR (Qargs_out_of_range, error_tail, "Args out of range"); |
| 4167 | PUT_ERROR (Qvoid_function, error_tail, | 4145 | PUT_ERROR (Qvoid_function, error_tail, |
| 4168 | "Symbol's function definition is void"); | 4146 | "Symbol's function definition is void"); |
| @@ -27,6 +27,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 27 | #include <vla.h> | 27 | #include <vla.h> |
| 28 | #include <errno.h> | 28 | #include <errno.h> |
| 29 | #include <ctype.h> | 29 | #include <ctype.h> |
| 30 | #include <math.h> | ||
| 30 | 31 | ||
| 31 | #include "lisp.h" | 32 | #include "lisp.h" |
| 32 | #include "bignum.h" | 33 | #include "bignum.h" |
| @@ -466,21 +467,10 @@ load_unaligned_size_t (const void *p) | |||
| 466 | return x; | 467 | return x; |
| 467 | } | 468 | } |
| 468 | 469 | ||
| 469 | DEFUN ("string-lessp", Fstring_lessp, Sstring_lessp, 2, 2, 0, | 470 | /* Return -1/0/1 to indicate the relation </=/> between string1 and string2. */ |
| 470 | doc: /* Return non-nil if STRING1 is less than STRING2 in lexicographic order. | 471 | static int |
| 471 | Case is significant. | 472 | string_cmp (Lisp_Object string1, Lisp_Object string2) |
| 472 | Symbols are also allowed; their print names are used instead. */) | ||
| 473 | (Lisp_Object string1, Lisp_Object string2) | ||
| 474 | { | 473 | { |
| 475 | if (SYMBOLP (string1)) | ||
| 476 | string1 = SYMBOL_NAME (string1); | ||
| 477 | else | ||
| 478 | CHECK_STRING (string1); | ||
| 479 | if (SYMBOLP (string2)) | ||
| 480 | string2 = SYMBOL_NAME (string2); | ||
| 481 | else | ||
| 482 | CHECK_STRING (string2); | ||
| 483 | |||
| 484 | ptrdiff_t n = min (SCHARS (string1), SCHARS (string2)); | 474 | ptrdiff_t n = min (SCHARS (string1), SCHARS (string2)); |
| 485 | 475 | ||
| 486 | if ((!STRING_MULTIBYTE (string1) || SCHARS (string1) == SBYTES (string1)) | 476 | if ((!STRING_MULTIBYTE (string1) || SCHARS (string1) == SBYTES (string1)) |
| @@ -489,7 +479,9 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 489 | /* Each argument is either unibyte or all-ASCII multibyte: | 479 | /* Each argument is either unibyte or all-ASCII multibyte: |
| 490 | we can compare bytewise. */ | 480 | we can compare bytewise. */ |
| 491 | int d = memcmp (SSDATA (string1), SSDATA (string2), n); | 481 | int d = memcmp (SSDATA (string1), SSDATA (string2), n); |
| 492 | return d < 0 || (d == 0 && n < SCHARS (string2)) ? Qt : Qnil; | 482 | if (d) |
| 483 | return d; | ||
| 484 | return n < SCHARS (string2) ? -1 : n > SCHARS (string2); | ||
| 493 | } | 485 | } |
| 494 | else if (STRING_MULTIBYTE (string1) && STRING_MULTIBYTE (string2)) | 486 | else if (STRING_MULTIBYTE (string1) && STRING_MULTIBYTE (string2)) |
| 495 | { | 487 | { |
| @@ -523,7 +515,7 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 523 | 515 | ||
| 524 | if (b >= nb) | 516 | if (b >= nb) |
| 525 | /* One string is a prefix of the other. */ | 517 | /* One string is a prefix of the other. */ |
| 526 | return b < nb2 ? Qt : Qnil; | 518 | return b < nb2 ? -1 : b > nb2; |
| 527 | 519 | ||
| 528 | /* Now back up to the start of the differing characters: | 520 | /* Now back up to the start of the differing characters: |
| 529 | it's the last byte not having the bit pattern 10xxxxxx. */ | 521 | it's the last byte not having the bit pattern 10xxxxxx. */ |
| @@ -535,7 +527,7 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 535 | ptrdiff_t i1_byte = b, i2_byte = b; | 527 | ptrdiff_t i1_byte = b, i2_byte = b; |
| 536 | int c1 = fetch_string_char_advance_no_check (string1, &i1, &i1_byte); | 528 | int c1 = fetch_string_char_advance_no_check (string1, &i1, &i1_byte); |
| 537 | int c2 = fetch_string_char_advance_no_check (string2, &i2, &i2_byte); | 529 | int c2 = fetch_string_char_advance_no_check (string2, &i2, &i2_byte); |
| 538 | return c1 < c2 ? Qt : Qnil; | 530 | return c1 < c2 ? -1 : c1 > c2; |
| 539 | } | 531 | } |
| 540 | else if (STRING_MULTIBYTE (string1)) | 532 | else if (STRING_MULTIBYTE (string1)) |
| 541 | { | 533 | { |
| @@ -546,9 +538,9 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 546 | int c1 = fetch_string_char_advance_no_check (string1, &i1, &i1_byte); | 538 | int c1 = fetch_string_char_advance_no_check (string1, &i1, &i1_byte); |
| 547 | int c2 = SREF (string2, i2++); | 539 | int c2 = SREF (string2, i2++); |
| 548 | if (c1 != c2) | 540 | if (c1 != c2) |
| 549 | return c1 < c2 ? Qt : Qnil; | 541 | return c1 < c2 ? -1 : 1; |
| 550 | } | 542 | } |
| 551 | return i1 < SCHARS (string2) ? Qt : Qnil; | 543 | return i1 < SCHARS (string2) ? -1 : i1 > SCHARS (string2); |
| 552 | } | 544 | } |
| 553 | else | 545 | else |
| 554 | { | 546 | { |
| @@ -559,12 +551,30 @@ Symbols are also allowed; their print names are used instead. */) | |||
| 559 | int c1 = SREF (string1, i1++); | 551 | int c1 = SREF (string1, i1++); |
| 560 | int c2 = fetch_string_char_advance_no_check (string2, &i2, &i2_byte); | 552 | int c2 = fetch_string_char_advance_no_check (string2, &i2, &i2_byte); |
| 561 | if (c1 != c2) | 553 | if (c1 != c2) |
| 562 | return c1 < c2 ? Qt : Qnil; | 554 | return c1 < c2 ? -1 : 1; |
| 563 | } | 555 | } |
| 564 | return i1 < SCHARS (string2) ? Qt : Qnil; | 556 | return i1 < SCHARS (string2) ? -1 : i1 > SCHARS (string2); |
| 565 | } | 557 | } |
| 566 | } | 558 | } |
| 567 | 559 | ||
| 560 | DEFUN ("string-lessp", Fstring_lessp, Sstring_lessp, 2, 2, 0, | ||
| 561 | doc: /* Return non-nil if STRING1 is less than STRING2 in lexicographic order. | ||
| 562 | Case is significant. | ||
| 563 | Symbols are also allowed; their print names are used instead. */) | ||
| 564 | (Lisp_Object string1, Lisp_Object string2) | ||
| 565 | { | ||
| 566 | if (SYMBOLP (string1)) | ||
| 567 | string1 = SYMBOL_NAME (string1); | ||
| 568 | else | ||
| 569 | CHECK_STRING (string1); | ||
| 570 | if (SYMBOLP (string2)) | ||
| 571 | string2 = SYMBOL_NAME (string2); | ||
| 572 | else | ||
| 573 | CHECK_STRING (string2); | ||
| 574 | |||
| 575 | return string_cmp (string1, string2) < 0 ? Qt : Qnil; | ||
| 576 | } | ||
| 577 | |||
| 568 | DEFUN ("string-version-lessp", Fstring_version_lessp, | 578 | DEFUN ("string-version-lessp", Fstring_version_lessp, |
| 569 | Sstring_version_lessp, 2, 2, 0, | 579 | Sstring_version_lessp, 2, 2, 0, |
| 570 | doc: /* Return non-nil if S1 is less than S2, as version strings. | 580 | doc: /* Return non-nil if S1 is less than S2, as version strings. |
| @@ -2908,6 +2918,233 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum equal_kind equal_kind, | |||
| 2908 | 2918 | ||
| 2909 | return false; | 2919 | return false; |
| 2910 | } | 2920 | } |
| 2921 | |||
| 2922 | /* Return -1/0/1 for the </=/> lexicographic relation between bool-vectors. */ | ||
| 2923 | static int | ||
| 2924 | bool_vector_cmp (Lisp_Object a, Lisp_Object b) | ||
| 2925 | { | ||
| 2926 | ptrdiff_t na = bool_vector_size (a); | ||
| 2927 | ptrdiff_t nb = bool_vector_size (b); | ||
| 2928 | /* Skip equal words. */ | ||
| 2929 | ptrdiff_t words_min = min (na, nb) / BITS_PER_BITS_WORD; | ||
| 2930 | bits_word *ad = bool_vector_data (a); | ||
| 2931 | bits_word *bd = bool_vector_data (b); | ||
| 2932 | ptrdiff_t i = 0; | ||
| 2933 | while (i < words_min && ad[i] == bd[i]) | ||
| 2934 | i++; | ||
| 2935 | na -= i * BITS_PER_BITS_WORD; | ||
| 2936 | nb -= i * BITS_PER_BITS_WORD; | ||
| 2937 | eassume (na >= 0 && nb >= 0); | ||
| 2938 | if (nb == 0) | ||
| 2939 | return na != 0; | ||
| 2940 | if (na == 0) | ||
| 2941 | return -1; | ||
| 2942 | |||
| 2943 | bits_word aw = bits_word_to_host_endian (ad[i]); | ||
| 2944 | bits_word bw = bits_word_to_host_endian (bd[i]); | ||
| 2945 | bits_word xw = aw ^ bw; | ||
| 2946 | if (xw == 0) | ||
| 2947 | return na < nb ? -1 : na > nb; | ||
| 2948 | |||
| 2949 | bits_word d = xw & -xw; /* Isolate first difference. */ | ||
| 2950 | eassume (d != 0); | ||
| 2951 | return (d & aw) ? 1 : -1; | ||
| 2952 | } | ||
| 2953 | |||
| 2954 | /* Return -1, 0 or 1 to indicate whether a<b, a=b or a>b in the sense of value<. | ||
| 2955 | In particular 0 does not mean equality in the sense of Fequal, only | ||
| 2956 | that the arguments cannot be ordered yet they can be compared (same | ||
| 2957 | type). */ | ||
| 2958 | static int | ||
| 2959 | value_cmp (Lisp_Object a, Lisp_Object b, int maxdepth) | ||
| 2960 | { | ||
| 2961 | if (maxdepth < 0) | ||
| 2962 | error ("Maximum depth exceeded in comparison"); | ||
| 2963 | |||
| 2964 | tail_recurse: | ||
| 2965 | /* Shortcut for a common case. */ | ||
| 2966 | if (BASE_EQ (a, b)) | ||
| 2967 | return 0; | ||
| 2968 | |||
| 2969 | switch (XTYPE (a)) | ||
| 2970 | { | ||
| 2971 | case_Lisp_Int: | ||
| 2972 | { | ||
| 2973 | EMACS_INT ia = XFIXNUM (a); | ||
| 2974 | if (FIXNUMP (b)) | ||
| 2975 | return ia < XFIXNUM (b) ? -1 : 1; /* we know that a≠b */ | ||
| 2976 | if (FLOATP (b)) | ||
| 2977 | return ia < XFLOAT_DATA (b) ? -1 : ia > XFLOAT_DATA (b); | ||
| 2978 | if (BIGNUMP (b)) | ||
| 2979 | return -mpz_sgn (*xbignum_val (b)); | ||
| 2980 | } | ||
| 2981 | goto type_mismatch; | ||
| 2982 | |||
| 2983 | case Lisp_Symbol: | ||
| 2984 | if (BARE_SYMBOL_P (b)) | ||
| 2985 | return string_cmp (XBARE_SYMBOL (a)->u.s.name, | ||
| 2986 | XBARE_SYMBOL (b)->u.s.name); | ||
| 2987 | if (CONSP (b) && NILP (a)) | ||
| 2988 | return -1; | ||
| 2989 | if (SYMBOLP (b)) | ||
| 2990 | /* Slow-path branch when B is a symbol-with-pos. */ | ||
| 2991 | return string_cmp (XBARE_SYMBOL (a)->u.s.name, XSYMBOL (b)->u.s.name); | ||
| 2992 | goto type_mismatch; | ||
| 2993 | |||
| 2994 | case Lisp_String: | ||
| 2995 | if (STRINGP (b)) | ||
| 2996 | return string_cmp (a, b); | ||
| 2997 | goto type_mismatch; | ||
| 2998 | |||
| 2999 | case Lisp_Cons: | ||
| 3000 | /* FIXME: Optimise for difference in the first element? */ | ||
| 3001 | FOR_EACH_TAIL (b) | ||
| 3002 | { | ||
| 3003 | int cmp = value_cmp (XCAR (a), XCAR (b), maxdepth - 1); | ||
| 3004 | if (cmp != 0) | ||
| 3005 | return cmp; | ||
| 3006 | a = XCDR (a); | ||
| 3007 | if (!CONSP (a)) | ||
| 3008 | { | ||
| 3009 | b = XCDR (b); | ||
| 3010 | goto tail_recurse; | ||
| 3011 | } | ||
| 3012 | } | ||
| 3013 | if (NILP (b)) | ||
| 3014 | return 1; | ||
| 3015 | else | ||
| 3016 | goto type_mismatch; | ||
| 3017 | goto tail_recurse; | ||
| 3018 | |||
| 3019 | case Lisp_Vectorlike: | ||
| 3020 | if (VECTORLIKEP (b)) | ||
| 3021 | { | ||
| 3022 | enum pvec_type ta = PSEUDOVECTOR_TYPE (XVECTOR (a)); | ||
| 3023 | enum pvec_type tb = PSEUDOVECTOR_TYPE (XVECTOR (b)); | ||
| 3024 | if (ta == tb) | ||
| 3025 | switch (ta) | ||
| 3026 | { | ||
| 3027 | case PVEC_NORMAL_VECTOR: | ||
| 3028 | case PVEC_RECORD: | ||
| 3029 | { | ||
| 3030 | ptrdiff_t len_a = ASIZE (a); | ||
| 3031 | ptrdiff_t len_b = ASIZE (b); | ||
| 3032 | if (ta == PVEC_RECORD) | ||
| 3033 | { | ||
| 3034 | len_a &= PSEUDOVECTOR_SIZE_MASK; | ||
| 3035 | len_b &= PSEUDOVECTOR_SIZE_MASK; | ||
| 3036 | } | ||
| 3037 | ptrdiff_t len_min = min (len_a, len_b); | ||
| 3038 | for (ptrdiff_t i = 0; i < len_min; i++) | ||
| 3039 | { | ||
| 3040 | int cmp = value_cmp (AREF (a, i), AREF (b, i), | ||
| 3041 | maxdepth - 1); | ||
| 3042 | if (cmp != 0) | ||
| 3043 | return cmp; | ||
| 3044 | } | ||
| 3045 | return len_a < len_b ? -1 : len_a > len_b; | ||
| 3046 | } | ||
| 3047 | |||
| 3048 | case PVEC_BOOL_VECTOR: | ||
| 3049 | return bool_vector_cmp (a, b); | ||
| 3050 | |||
| 3051 | case PVEC_MARKER: | ||
| 3052 | { | ||
| 3053 | Lisp_Object buf_a = Fmarker_buffer (a); | ||
| 3054 | Lisp_Object buf_b = Fmarker_buffer (b); | ||
| 3055 | if (NILP (buf_a)) | ||
| 3056 | return NILP (buf_b) ? 0 : -1; | ||
| 3057 | if (NILP (buf_b)) | ||
| 3058 | return 1; | ||
| 3059 | int cmp = value_cmp (buf_a, buf_b, maxdepth - 1); | ||
| 3060 | if (cmp != 0) | ||
| 3061 | return cmp; | ||
| 3062 | ptrdiff_t pa = XMARKER (a)->charpos; | ||
| 3063 | ptrdiff_t pb = XMARKER (b)->charpos; | ||
| 3064 | return pa < pb ? -1 : pa > pb; | ||
| 3065 | } | ||
| 3066 | |||
| 3067 | case PVEC_PROCESS: | ||
| 3068 | a = Fprocess_name (a); | ||
| 3069 | b = Fprocess_name (b); | ||
| 3070 | goto tail_recurse; | ||
| 3071 | |||
| 3072 | case PVEC_BUFFER: | ||
| 3073 | { | ||
| 3074 | /* Killed buffers lack names and sort before those alive. */ | ||
| 3075 | Lisp_Object na = Fbuffer_name (a); | ||
| 3076 | Lisp_Object nb = Fbuffer_name (b); | ||
| 3077 | if (NILP (na)) | ||
| 3078 | return NILP (nb) ? 0 : -1; | ||
| 3079 | if (NILP (nb)) | ||
| 3080 | return 1; | ||
| 3081 | a = na; | ||
| 3082 | b = nb; | ||
| 3083 | goto tail_recurse; | ||
| 3084 | } | ||
| 3085 | |||
| 3086 | case PVEC_BIGNUM: | ||
| 3087 | return mpz_cmp (*xbignum_val (a), *xbignum_val (b)); | ||
| 3088 | |||
| 3089 | case PVEC_SYMBOL_WITH_POS: | ||
| 3090 | /* Compare by name, enabled or not. */ | ||
| 3091 | a = XSYMBOL_WITH_POS_SYM (a); | ||
| 3092 | b = XSYMBOL_WITH_POS_SYM (b); | ||
| 3093 | goto tail_recurse; | ||
| 3094 | |||
| 3095 | default: | ||
| 3096 | /* Treat other types as unordered. */ | ||
| 3097 | return 0; | ||
| 3098 | } | ||
| 3099 | } | ||
| 3100 | else if (BIGNUMP (a)) | ||
| 3101 | return -value_cmp (b, a, maxdepth); | ||
| 3102 | else if (SYMBOL_WITH_POS_P (a) && symbols_with_pos_enabled) | ||
| 3103 | { | ||
| 3104 | a = XSYMBOL_WITH_POS_SYM (a); | ||
| 3105 | goto tail_recurse; | ||
| 3106 | } | ||
| 3107 | |||
| 3108 | goto type_mismatch; | ||
| 3109 | |||
| 3110 | case Lisp_Float: | ||
| 3111 | { | ||
| 3112 | double fa = XFLOAT_DATA (a); | ||
| 3113 | if (FLOATP (b)) | ||
| 3114 | return fa < XFLOAT_DATA (b) ? -1 : fa > XFLOAT_DATA (b); | ||
| 3115 | if (FIXNUMP (b)) | ||
| 3116 | return fa < XFIXNUM (b) ? -1 : fa > XFIXNUM (b); | ||
| 3117 | if (BIGNUMP (b)) | ||
| 3118 | { | ||
| 3119 | if (isnan (fa)) | ||
| 3120 | return 0; | ||
| 3121 | return -mpz_cmp_d (*xbignum_val (b), fa); | ||
| 3122 | } | ||
| 3123 | } | ||
| 3124 | goto type_mismatch; | ||
| 3125 | |||
| 3126 | default: | ||
| 3127 | eassume (0); | ||
| 3128 | } | ||
| 3129 | type_mismatch: | ||
| 3130 | xsignal2 (Qtype_mismatch, a, b); | ||
| 3131 | } | ||
| 3132 | |||
| 3133 | DEFUN ("value<", Fvaluelt, Svaluelt, 2, 2, 0, | ||
| 3134 | doc: /* Return non-nil if A precedes B in standard value order. | ||
| 3135 | A and B must have the same basic type. | ||
| 3136 | Numbers are compared with `<'. | ||
| 3137 | Strings and symbols are compared with `string-lessp'. | ||
| 3138 | Lists, vectors, bool-vectors and records are compared lexicographically. | ||
| 3139 | Markers are compared lexicographically by buffer and position. | ||
| 3140 | Buffers and processes are compared by name. | ||
| 3141 | Other types are considered unordered and the return value will be `nil'. */) | ||
| 3142 | (Lisp_Object a, Lisp_Object b) | ||
| 3143 | { | ||
| 3144 | int maxdepth = 20; /* FIXME: arbitrary value */ | ||
| 3145 | return value_cmp (a, b, maxdepth) < 0 ? Qt : Qnil; | ||
| 3146 | } | ||
| 3147 | |||
| 2911 | 3148 | ||
| 2912 | 3149 | ||
| 2913 | DEFUN ("fillarray", Ffillarray, Sfillarray, 2, 2, 0, | 3150 | DEFUN ("fillarray", Ffillarray, Sfillarray, 2, 2, 0, |
| @@ -6589,6 +6826,7 @@ For best results this should end in a space. */); | |||
| 6589 | defsubr (&Seql); | 6826 | defsubr (&Seql); |
| 6590 | defsubr (&Sequal); | 6827 | defsubr (&Sequal); |
| 6591 | defsubr (&Sequal_including_properties); | 6828 | defsubr (&Sequal_including_properties); |
| 6829 | defsubr (&Svaluelt); | ||
| 6592 | defsubr (&Sfillarray); | 6830 | defsubr (&Sfillarray); |
| 6593 | defsubr (&Sclear_string); | 6831 | defsubr (&Sclear_string); |
| 6594 | defsubr (&Snconc); | 6832 | defsubr (&Snconc); |
diff --git a/src/lisp.h b/src/lisp.h index f86758c88fb..5583a7e2e8e 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -1882,6 +1882,30 @@ bool_vector_bytes (EMACS_INT size) | |||
| 1882 | return (size + BOOL_VECTOR_BITS_PER_CHAR - 1) / BOOL_VECTOR_BITS_PER_CHAR; | 1882 | return (size + BOOL_VECTOR_BITS_PER_CHAR - 1) / BOOL_VECTOR_BITS_PER_CHAR; |
| 1883 | } | 1883 | } |
| 1884 | 1884 | ||
| 1885 | INLINE bits_word | ||
| 1886 | bits_word_to_host_endian (bits_word val) | ||
| 1887 | { | ||
| 1888 | #ifndef WORDS_BIGENDIAN | ||
| 1889 | return val; | ||
| 1890 | #else | ||
| 1891 | if (BITS_WORD_MAX >> 31 == 1) | ||
| 1892 | return bswap_32 (val); | ||
| 1893 | if (BITS_WORD_MAX >> 31 >> 31 >> 1 == 1) | ||
| 1894 | return bswap_64 (val); | ||
| 1895 | { | ||
| 1896 | int i; | ||
| 1897 | bits_word r = 0; | ||
| 1898 | for (i = 0; i < sizeof val; i++) | ||
| 1899 | { | ||
| 1900 | r = ((r << 1 << (CHAR_BIT - 1)) | ||
| 1901 | | (val & ((1u << 1 << (CHAR_BIT - 1)) - 1))); | ||
| 1902 | val = val >> 1 >> (CHAR_BIT - 1); | ||
| 1903 | } | ||
| 1904 | return r; | ||
| 1905 | } | ||
| 1906 | #endif | ||
| 1907 | } | ||
| 1908 | |||
| 1885 | INLINE bool | 1909 | INLINE bool |
| 1886 | BOOL_VECTOR_P (Lisp_Object a) | 1910 | BOOL_VECTOR_P (Lisp_Object a) |
| 1887 | { | 1911 | { |