Skip to content

Commit 260f595

Browse files
committed
FEAT: implement SORT/COMPARE on any-string! values
fixes: Oldes/Rebol-issues#1100
1 parent b7eb572 commit 260f595

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

src/core/t-block.c

-1
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,6 @@ static struct {
428428
if (VAL_DECIMAL(val) == 0) return 0;
429429
return -1;
430430
}
431-
if (IS_TRUE(val)) return 1;
432431
return -1;
433432
}
434433

src/core/t-string.c

+79-1
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,67 @@ static REBSER *make_binary(REBVAL *arg, REBOOL make)
326326
return ((int)LO_CASE(*(REBYTE*)v2)) - ((int)LO_CASE(*(REBYTE*)v1));
327327
}
328328

329+
// WARNING! Not re-entrant. !!! Must find a way to push it on stack?
330+
static struct {
331+
REBFLG cased;
332+
REBFLG reverse;
333+
REBCNT offset;
334+
REBVAL *compare;
335+
REBFLG wide;
336+
} sort_flags = {0};
337+
338+
/***********************************************************************
339+
**
340+
*/ static int Compare_Call(const void *p1, const void *p2)
341+
/*
342+
***********************************************************************/
343+
{
344+
REBVAL *v1;
345+
REBVAL *v2;
346+
REBVAL *val;
347+
REBVAL *tmp;
348+
349+
// O: is there better way how to temporary use 2 values?
350+
DS_SKIP; v1 = DS_TOP;
351+
DS_SKIP; v2 = DS_TOP;
352+
353+
if (sort_flags.wide) {
354+
SET_CHAR(v1, (int)(*(REBCHR*)p2));
355+
SET_CHAR(v2, (int)(*(REBCHR*)p1));
356+
} else {
357+
SET_CHAR(v1, (int)(*(REBYTE*)p2));
358+
SET_CHAR(v2, (int)(*(REBYTE*)p1));
359+
}
360+
361+
if (sort_flags.reverse) {
362+
tmp = v1;
363+
v1 = v2;
364+
v2 = tmp;
365+
}
366+
367+
val = Apply_Func(0, sort_flags.compare, v1, v2, 0);
368+
369+
// v1 and v2 no more needed...
370+
DS_POP;
371+
DS_POP;
372+
373+
if (IS_LOGIC(val)) {
374+
if (IS_TRUE(val)) return 1;
375+
return -1;
376+
}
377+
if (IS_INTEGER(val)) {
378+
if (VAL_INT64(val) < 0) return 1;
379+
if (VAL_INT64(val) == 0) return 0;
380+
return -1;
381+
}
382+
if (IS_DECIMAL(val)) {
383+
if (VAL_DECIMAL(val) < 0) return 1;
384+
if (VAL_DECIMAL(val) == 0) return 0;
385+
return -1;
386+
}
387+
return -1;
388+
}
389+
329390

330391
/***********************************************************************
331392
**
@@ -336,6 +397,7 @@ static REBSER *make_binary(REBVAL *arg, REBOOL make)
336397
REBCNT len;
337398
REBCNT skip = 1;
338399
REBCNT size = 1;
400+
REBSER *args;
339401
int (*sfunc)(const void *v1, const void *v2);
340402

341403
// Determine length of sort:
@@ -351,7 +413,23 @@ static REBSER *make_binary(REBVAL *arg, REBOOL make)
351413

352414
// Use fast quicksort library function:
353415
if (skip > 1) len /= skip, size *= skip;
354-
if (ccase) {
416+
417+
if (ANY_FUNC(compv)) {
418+
// Check argument types of comparator function.
419+
args = VAL_FUNC_ARGS(compv);
420+
if (BLK_LEN(args) > 1 && !TYPE_CHECK(BLK_SKIP(args, 1), REB_CHAR))
421+
Trap3(RE_EXPECT_ARG, Of_Type(compv), BLK_SKIP(args, 1), Get_Type_Word(REB_CHAR));
422+
if (BLK_LEN(args) > 2 && !TYPE_CHECK(BLK_SKIP(args, 2), REB_CHAR))
423+
Trap3(RE_EXPECT_ARG, Of_Type(compv), BLK_SKIP(args, 2), Get_Type_Word(REB_CHAR));
424+
sort_flags.cased = ccase;
425+
sort_flags.reverse = rev;
426+
sort_flags.compare = 0;
427+
sort_flags.offset = 0;
428+
sort_flags.compare = compv;
429+
sort_flags.wide = 1 < SERIES_WIDE(VAL_SERIES(string));
430+
sfunc = Compare_Call;
431+
432+
} else if (ccase) {
355433
sfunc = rev ? Compare_Chr_Cased_Rev : Compare_Chr_Cased;
356434
} else {
357435
sfunc = rev ? Compare_Chr_Uncased_Rev : Compare_Chr_Uncased;

src/tests/units/series-test.r3

+9-1
Original file line numberDiff line numberDiff line change
@@ -568,12 +568,20 @@ Rebol [
568568
--assert "ABCDEFabcdef" == sort/case s
569569
--assert "AaBbCcDdEeFf" == sort s
570570

571-
--test-- "SORT/compare"
571+
--test-- "SORT/compare block!"
572572
;@@ https://github.com/Oldes/Rebol-issues/issues/720
573573
--assert [3 2 1] = sort/compare [1 2 3] func [a b] [a > b]
574574
;@@ https://github.com/Oldes/Rebol-issues/issues/2376
575575
--assert [1 3 10] = sort/compare [1 10 3] func[x y][case [x > y [1] x < y [-1] true [0]]]
576576

577+
--test-- "SORT/compare string!"
578+
;@@ https://github.com/Oldes/Rebol-issues/issues/1100
579+
comp: func [a b] [a > b]
580+
--assert "zyxcba" == sort/compare "abczyx" :comp
581+
--assert %54321 == sort/compare %21543 :comp
582+
--assert #{050403020100} == sort/compare #{000102030405} :comp
583+
--assert "šřba" == sort/compare "ašbř" :comp
584+
577585
--test-- "SORT/skip/compare"
578586
;@@ https://github.com/Oldes/Rebol-issues/issues/1152
579587
--assert ["A" "a"] = sort/compare ["A" "a"] func [a b] [a < b]

0 commit comments

Comments
 (0)