aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattias Engdegård2024-03-10 13:18:22 +0100
committerMattias Engdegård2024-03-29 11:39:38 +0100
commit1232ab31c656b8564984a758957466f90ac10501 (patch)
tree38a7774207a5ac8dba2612bef9a6a39f3cd0d658 /src
parentc3684b97885c5a1f4d0713ff45c7395e9a4c6e8a (diff)
downloademacs-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.c26
-rw-r--r--src/fns.c280
-rw-r--r--src/lisp.h24
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
3838static bits_word
3839bits_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
3862DEFUN ("bool-vector-exclusive-or", Fbool_vector_exclusive_or, 3838DEFUN ("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");
diff --git a/src/fns.c b/src/fns.c
index 0a64e515402..7faf25b9088 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -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
469DEFUN ("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. 471static int
471Case is significant. 472string_cmp (Lisp_Object string1, Lisp_Object string2)
472Symbols 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
560DEFUN ("string-lessp", Fstring_lessp, Sstring_lessp, 2, 2, 0,
561 doc: /* Return non-nil if STRING1 is less than STRING2 in lexicographic order.
562Case is significant.
563Symbols 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
568DEFUN ("string-version-lessp", Fstring_version_lessp, 578DEFUN ("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. */
2923static int
2924bool_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). */
2958static int
2959value_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
3133DEFUN ("value<", Fvaluelt, Svaluelt, 2, 2, 0,
3134 doc: /* Return non-nil if A precedes B in standard value order.
3135A and B must have the same basic type.
3136Numbers are compared with `<'.
3137Strings and symbols are compared with `string-lessp'.
3138Lists, vectors, bool-vectors and records are compared lexicographically.
3139Markers are compared lexicographically by buffer and position.
3140Buffers and processes are compared by name.
3141Other 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
2913DEFUN ("fillarray", Ffillarray, Sfillarray, 2, 2, 0, 3150DEFUN ("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
1885INLINE bits_word
1886bits_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
1885INLINE bool 1909INLINE bool
1886BOOL_VECTOR_P (Lisp_Object a) 1910BOOL_VECTOR_P (Lisp_Object a)
1887{ 1911{