mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Deprecate builtin EMPTY_MACRO_SLOT for optional macro arguments (#2805)
* add deprecations to macro slot builtins * refactor all stdlib uses of now-deprecated EMPTY_MACRO_SLOT; release notes * update incorrect releasenotes ref to this pr * remove leftover comments from refactoring * remove unnecessary `EmptySlot`-like type in countingsort; use private macro directly
This commit is contained in:
@@ -6,14 +6,14 @@ in [0, array.len) where x would be inserted or cmp(i) is true and cmp(j) is true
|
||||
|
||||
@require @list_is_by_ref(list) : "Expected a list passed by reference or a slice"
|
||||
@require @is_sortable(list) : "The list must be sortable"
|
||||
@require @is_valid_cmp_fn(cmp, list, context) : "Expected a comparison function which compares values"
|
||||
@require @is_valid_context(cmp, context) : "Expected a valid context"
|
||||
@require @is_valid_cmp_fn(#cmp: ...cmp, #list: list, #context: ...context) : "Expected a comparison function which compares values"
|
||||
@require @is_valid_context(...cmp, ...context) : "Expected a valid context"
|
||||
*>
|
||||
macro usz binarysearch(list, x, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||
macro usz binarysearch(list, x, cmp = ..., context = ...) @builtin
|
||||
{
|
||||
usz i;
|
||||
var $no_cmp = @is_empty_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(context);
|
||||
var $no_cmp = !$defined(cmp);
|
||||
var $has_context = $defined(context);
|
||||
|
||||
$if $kindof(list) == SLICE:
|
||||
usz len = lengthof(list);
|
||||
@@ -52,41 +52,41 @@ macro usz binarysearch(list, x, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SL
|
||||
$endif
|
||||
}
|
||||
$else
|
||||
usz len = lengthof(*list);
|
||||
for (usz j = len; i < j;)
|
||||
{
|
||||
usz half = i + (j - i) / 2;
|
||||
$if $no_cmp:
|
||||
switch
|
||||
{
|
||||
case greater((*list)[half], x): j = half;
|
||||
case less((*list)[half], x): i = half + 1;
|
||||
default: return half;
|
||||
}
|
||||
$else
|
||||
usz len = lengthof(*list);
|
||||
for (usz j = len; i < j;)
|
||||
{
|
||||
usz half = i + (j - i) / 2;
|
||||
$if $no_cmp:
|
||||
switch
|
||||
{
|
||||
case greater((*list)[half], x): j = half;
|
||||
case less((*list)[half], x): i = half + 1;
|
||||
default: return half;
|
||||
}
|
||||
$else
|
||||
|
||||
$switch:
|
||||
$case $defined(cmp((*list)[0], (*list)[0], context)):
|
||||
int res = cmp(list[half], x, context);
|
||||
$case $defined(cmp((*list)[0], (*list)[0])):
|
||||
assert(!$has_context);
|
||||
int res = cmp((*list)[half], x);
|
||||
$case $defined(cmp(&(*list)[0], &(*list)[0], context)):
|
||||
int res = cmp(&(*list)[half], &x, context);
|
||||
$case $defined(cmp(&(*list)[0], &(*list)[0])):
|
||||
assert(!$has_context);
|
||||
int res = cmp(&(*list)[half], &x);
|
||||
$default:
|
||||
assert(false, "Invalid comparison function");
|
||||
$endswitch
|
||||
switch
|
||||
{
|
||||
case res > 0: j = half;
|
||||
case res < 0: i = half + 1;
|
||||
default: return half;
|
||||
}
|
||||
$endif
|
||||
}
|
||||
$switch:
|
||||
$case $defined(cmp((*list)[0], (*list)[0], context)):
|
||||
int res = cmp(list[half], x, context);
|
||||
$case $defined(cmp((*list)[0], (*list)[0])):
|
||||
assert(!$has_context);
|
||||
int res = cmp((*list)[half], x);
|
||||
$case $defined(cmp(&(*list)[0], &(*list)[0], context)):
|
||||
int res = cmp(&(*list)[half], &x, context);
|
||||
$case $defined(cmp(&(*list)[0], &(*list)[0])):
|
||||
assert(!$has_context);
|
||||
int res = cmp(&(*list)[half], &x);
|
||||
$default:
|
||||
assert(false, "Invalid comparison function");
|
||||
$endswitch
|
||||
switch
|
||||
{
|
||||
case res > 0: j = half;
|
||||
case res < 0: i = half + 1;
|
||||
default: return half;
|
||||
}
|
||||
$endif
|
||||
}
|
||||
$endif
|
||||
return i;
|
||||
return i;
|
||||
}
|
||||
@@ -1,41 +1,44 @@
|
||||
module std::sort;
|
||||
import std::sort::is;
|
||||
import std::sort::cs;
|
||||
import std::sort::cs @public;
|
||||
import std::sort::qs;
|
||||
|
||||
<*
|
||||
|
||||
Sort list using the counting sort algorithm.
|
||||
|
||||
@require @list_is_by_ref(list) : "Expected the list to be passed by reference"
|
||||
@require @is_sortable(list) : "The list must be indexable and support .len or .len()"
|
||||
@require @is_cmp_key_fn(key_fn, list) : "Expected a transformation function which returns an unsigned integer."
|
||||
@require @is_cmp_key_fn(#key_fn: ...key_fn, #list: list) : "Expected a transformation function which returns an unsigned integer."
|
||||
*>
|
||||
macro void countingsort(list, key_fn = EMPTY_MACRO_SLOT) @builtin
|
||||
macro void countingsort(list, key_fn = ...) @builtin
|
||||
{
|
||||
$if $kindof(list) == SLICE:
|
||||
cs::csort{$typeof(list), $typeof(key_fn)}(list, 0, list.len, key_fn, ~((uint)0));
|
||||
$else
|
||||
cs::csort{$typeof(*list), $typeof(key_fn)}(list, 0, lengthof(*list), key_fn, ~((uint)0));
|
||||
$endif
|
||||
var list_length = $kindof(list) == SLICE ??? list.len : lengthof(*list);
|
||||
var $ListType = $kindof(list) == SLICE ??? $typeof(list) : $typeof(*list);
|
||||
cs::_csort{$ListType, $defined(key_fn) ??? $typeof(key_fn) : void*}(list, 0, list_length, ~(uint)0, ...key_fn);
|
||||
}
|
||||
|
||||
macro void insertionsort_indexed(list, start, end, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||
macro void insertionsort_indexed(list, start, end, cmp = ..., context = ...) @builtin
|
||||
{
|
||||
// When the context or cmp functions are not defined, we can simply use a dummy/default type.
|
||||
var $CmpFnType = $defined(cmp) ??? $typeof(cmp) : uint;
|
||||
var $ContextType = $defined(context) ??? $typeof(context) : uint;
|
||||
$if $kindof(list) == SLICE:
|
||||
is::isort{$typeof((list)), $typeof(cmp), $typeof(context)}(list, (usz)start, (usz)end, cmp, context);
|
||||
is::isort{$typeof((list)), $CmpFnType, $ContextType}(list, (usz)start, (usz)end, ...cmp, ...context);
|
||||
$else
|
||||
is::isort{$typeof((*list)), $typeof(cmp), $typeof(context)}(list, (usz)start, (usz)end, cmp, context);
|
||||
is::isort{$typeof((*list)), $CmpFnType, $ContextType}(list, (usz)start, (usz)end, ...cmp, ...context);
|
||||
$endif
|
||||
|
||||
}
|
||||
|
||||
macro void quicksort_indexed(list, start, end, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||
macro void quicksort_indexed(list, start, end, cmp = ..., context = ...) @builtin
|
||||
{
|
||||
// When the context or cmp functions are not defined, we can simply use a dummy/default type.
|
||||
var $CmpFnType = $defined(cmp) ??? $typeof(cmp) : uint;
|
||||
var $ContextType = $defined(context) ??? $typeof(context) : uint;
|
||||
$if $kindof(list) == SLICE:
|
||||
qs::qsort{$typeof((list)), $typeof(cmp), $typeof(context)}(list, (isz)start, (isz)(end-1), cmp, context);
|
||||
qs::qsort{$typeof((list)), $CmpFnType, $ContextType}(list, (isz)start, (isz)(end-1), ...cmp, ...context);
|
||||
$else
|
||||
qs::qsort{$typeof((*list)), $typeof(cmp), $typeof(context)}(list, (isz)start, (isz)(end-1), cmp, context);
|
||||
qs::qsort{$typeof((*list)), $CmpFnType, $ContextType}(list, (isz)start, (isz)(end-1), ...cmp, ...context);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -46,7 +49,7 @@ alias Ranges @private = usz[257];
|
||||
alias Indexs @private = char[256];
|
||||
alias ElementType = $typeof((Type){}[0]);
|
||||
|
||||
const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot);
|
||||
const bool NO_KEY_FN @private = KeyFn.kindof != FUNC;
|
||||
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $defined($typefrom(KeyFn.paramsof[0].type) x = (ElementType){});
|
||||
const bool LIST_HAS_REF @private = $defined(&(Type){}[0]);
|
||||
|
||||
@@ -64,7 +67,26 @@ macro list_get(ListType l, i) @if(IS_SLICE) => l[i];
|
||||
macro list_get_ref(ListType l, i) @if(!IS_SLICE) => &(*l)[i];
|
||||
macro list_get_ref(ListType l, i) @if(IS_SLICE) => &l[i];
|
||||
|
||||
fn void csort(ListType list, usz low, usz high, KeyFn key_fn, uint byte_idx)
|
||||
fn void csort(ListType list, usz low, usz high, KeyFn key_fn, uint byte_idx) @if (!NO_KEY_FN)
|
||||
{
|
||||
_csort(list, low, high, byte_idx, key_fn);
|
||||
}
|
||||
|
||||
fn void csort(ListType list, usz low, usz high, uint byte_idx) @if (NO_KEY_FN)
|
||||
{
|
||||
_csort(list, low, high, byte_idx);
|
||||
}
|
||||
|
||||
<*
|
||||
COMPATIBILITY: With the deprecation of `EMPTY_MACRO_SLOT` in 0.7.9, this inner function was introduced
|
||||
to prevent needing to further deprecate this module's downstream API. Since `key_fn` in `csort` appears
|
||||
inline with other position-based parameters, it would be a hassle to require all callers of `csort` to
|
||||
use named parameters when supplying `key_fn`.
|
||||
|
||||
In other words, `csort(a_list, low, high, key_fn: some_fn, byte_idx: index_val)` breaks the existing API
|
||||
since explicitly naming `byte_idx` becomes a requirement.
|
||||
*>
|
||||
macro void _csort(ListType list, usz low, usz high, uint byte_idx, KeyFn key_fn = ...) @private
|
||||
{
|
||||
if (high <= low) return;
|
||||
$if NO_KEY_FN:
|
||||
@@ -182,7 +204,7 @@ fn void csort(ListType list, usz low, usz high, KeyFn key_fn, uint byte_idx)
|
||||
usz start_offset = ranges[i];
|
||||
usz end_offset = ranges[i + 1];
|
||||
|
||||
insertionsort_indexed(list, low + start_offset, low + end_offset, compare_fn, key_fn);
|
||||
insertionsort_indexed(list, low + start_offset, low + end_offset, compare_fn, ...key_fn);
|
||||
}
|
||||
|
||||
for (usz p = 0; p < fallback1_count; p++) {
|
||||
@@ -191,7 +213,7 @@ fn void csort(ListType list, usz low, usz high, KeyFn key_fn, uint byte_idx)
|
||||
usz start_offset = ranges[i];
|
||||
usz end_offset = ranges[i + 1];
|
||||
|
||||
quicksort_indexed(list, low + start_offset, low + end_offset, compare_fn, key_fn);
|
||||
quicksort_indexed(list, low + start_offset, low + end_offset, compare_fn, ...key_fn);
|
||||
}
|
||||
|
||||
for (usz p = 0; p < recursion_count; p++)
|
||||
@@ -201,7 +223,11 @@ fn void csort(ListType list, usz low, usz high, KeyFn key_fn, uint byte_idx)
|
||||
usz start_offset = ranges[i];
|
||||
usz end_offset = ranges[i + 1];
|
||||
|
||||
csort(list, low + start_offset, low + end_offset, key_fn, byte_idx - 1);
|
||||
$if $defined(key_fn):
|
||||
csort(list, low + start_offset, low + end_offset, key_fn, byte_idx - 1);
|
||||
$else
|
||||
csort(list, low + start_offset, low + end_offset, byte_idx - 1);
|
||||
$endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,17 @@ import std::sort::is;
|
||||
|
||||
@require @list_is_by_ref(list) : "Expected a list passed by reference, or slice passed by value"
|
||||
@require @is_sortable(list) : "The list must be indexable and support .len or .len()"
|
||||
@require @is_valid_cmp_fn(cmp, list, context) : "Expected a comparison function which compares values"
|
||||
@require @is_valid_cmp_fn(#cmp: ...cmp, #list: list, #context: ...context) : "Expected a comparison function which compares values"
|
||||
*>
|
||||
macro void insertionsort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin @safemacro
|
||||
macro void insertionsort(list, cmp = ..., context = ...) @builtin @safemacro
|
||||
{
|
||||
// When the context or cmp functions are not defined, we can simply use a dummy/default type.
|
||||
var $CmpFnType = $defined(cmp) ??? $typeof(cmp) : uint;
|
||||
var $ContextType = $defined(context) ??? $typeof(context) : uint;
|
||||
$if $kindof(list) == SLICE:
|
||||
is::isort{$typeof(list), $typeof(cmp), $typeof(context)}(list, 0, lengthof(list), cmp, context);
|
||||
is::isort{$typeof(list), $CmpFnType, $ContextType}(list, 0, lengthof(list), ...cmp, ...context);
|
||||
$else
|
||||
is::isort{$typeof(*list), $typeof(cmp), $typeof(context)}(list, 0, lengthof(*list), cmp, context);
|
||||
is::isort{$typeof(*list), $CmpFnType, $ContextType}(list, 0, lengthof(*list), ...cmp, ...context);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -25,10 +28,10 @@ alias ListType = $typefrom(IS_SLICE ??? Type : Type*);
|
||||
macro ElementType list_get(ListType l, i) => IS_SLICE ??? l[i] : (*l)[i];
|
||||
macro ElementType* list_get_ref(ListType l, i) => IS_SLICE ??? &l[i] : &(*l)[i];
|
||||
|
||||
fn void isort(ListType list, usz low, usz high, CmpFn comp, Context context)
|
||||
macro void isort(ListType list, usz low, usz high, CmpFn comp = ..., Context context = ...)
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(comp);
|
||||
var $has_context = @is_valid_macro_slot(context);
|
||||
var $has_cmp = $defined(comp);
|
||||
var $has_context = $defined(context);
|
||||
var $cmp_by_value = $has_cmp &&& $defined($typefrom(CmpFn.paramsof[0].type) p = list_get(list, 0));
|
||||
var $has_get_ref = IS_SLICE ||| $defined(&(*list)[0]);
|
||||
for (usz i = low; i < high; ++i)
|
||||
|
||||
@@ -6,15 +6,18 @@ import std::sort::qs;
|
||||
|
||||
@require @list_is_by_ref(list) : "Expected a list passed by reference or be a slice"
|
||||
@require @is_sortable(list) : "The list must be indexable and support .len or .len()"
|
||||
@require @is_valid_cmp_fn(cmp, list, context) : "Expected a comparison function which compares values"
|
||||
@require @is_valid_context(cmp, context) : "Expected a valid context"
|
||||
@require @is_valid_cmp_fn(#cmp: ...cmp, #list: list, #context: ...context) : "Expected a comparison function which compares values"
|
||||
@require @is_valid_context(...cmp, ...context) : "Expected a valid context"
|
||||
*>
|
||||
macro void quicksort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||
macro void quicksort(list, cmp = ..., context = ...) @builtin
|
||||
{
|
||||
// When the context or cmp functions are not defined, we can simply use a dummy/default type.
|
||||
var $CmpFnType = $defined(cmp) ??? $typeof(cmp) : uint;
|
||||
var $ContextType = $defined(context) ??? $typeof(context) : uint;
|
||||
$if $kindof(list) == SLICE:
|
||||
qs::qsort{$typeof(list), $typeof(cmp), $typeof(context)}(list, 0, (isz)list.len - 1, cmp, context);
|
||||
qs::qsort{$typeof(list), $CmpFnType, $ContextType}(list, 0, (isz)list.len - 1, ...cmp, ...context);
|
||||
$else
|
||||
qs::qsort{$typeof(*list), $typeof(cmp), $typeof(context)}(list, 0, (isz)lengthof(*list) - 1, cmp, context);
|
||||
qs::qsort{$typeof(*list), $CmpFnType, $ContextType}(list, 0, (isz)lengthof(*list) - 1, ...cmp, ...context);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -25,15 +28,18 @@ macro void quicksort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @
|
||||
|
||||
@require @list_is_by_ref(list) : "Expected a list passed by reference or be a slice"
|
||||
@require @is_sortable(list) : "The list must be indexable and support .len or .len()"
|
||||
@require @is_valid_cmp_fn(cmp, list, context) : "expected a comparison function which compares values"
|
||||
@require @is_valid_context(cmp, context) : "Expected a valid context"
|
||||
@require @is_valid_cmp_fn(#cmp: ...cmp, #list: list, #context: ...context) : "expected a comparison function which compares values"
|
||||
@require @is_valid_context(...cmp, ...context) : "Expected a valid context"
|
||||
*>
|
||||
macro quickselect(list, isz k, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
|
||||
macro quickselect(list, isz k, cmp = ..., context = ...) @builtin
|
||||
{
|
||||
// When the context or cmp functions are not defined, we can simply use a dummy/default type.
|
||||
var $CmpFnType = $defined(cmp) ??? $typeof(cmp) : uint;
|
||||
var $ContextType = $defined(context) ??? $typeof(context) : uint;
|
||||
$if $kindof(list) == SLICE:
|
||||
return qs::qselect{$typeof(list), $typeof(cmp), $typeof(context)}(list, 0, (isz)list.len - 1, k, cmp, context);
|
||||
return qs::qselect{$typeof(list), $CmpFnType, $ContextType}(list, 0, (isz)list.len - 1, k, ...cmp, ...context);
|
||||
$else
|
||||
return qs::qselect{$typeof(*list), $typeof(cmp), $typeof(context)}(list, 0, (isz)lengthof(*list) - 1, k, cmp, context);
|
||||
return qs::qselect{$typeof(*list), $CmpFnType, $ContextType}(list, 0, (isz)lengthof(*list) - 1, k, ...cmp, ...context);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -59,7 +65,7 @@ alias Stack @private = StackElementItem[64];
|
||||
|
||||
// Based on https://alienryderflex.com/quicksort by Darel Rex Finley, Public Domain.
|
||||
|
||||
fn void qsort(ListType list, isz low, isz high, CmpFn cmp, Context context)
|
||||
macro void qsort(ListType list, isz low, isz high, CmpFn cmp = ..., Context context = ...)
|
||||
{
|
||||
if (low >= 0 && high >= 0 && low < high)
|
||||
{
|
||||
@@ -76,7 +82,7 @@ fn void qsort(ListType list, isz low, isz high, CmpFn cmp, Context context)
|
||||
|
||||
if (l < h)
|
||||
{
|
||||
l = @partition(list, l, h, cmp, context);
|
||||
l = @partition(list, l, h, ...cmp, ...context);
|
||||
stack[i + 1].low = l + 1;
|
||||
stack[i + 1].high = stack[i].high;
|
||||
stack[i++].high = l;
|
||||
@@ -97,7 +103,7 @@ fn void qsort(ListType list, isz low, isz high, CmpFn cmp, Context context)
|
||||
@require low <= k : "kth smallest element is smaller than lower bounds"
|
||||
@require k <= high : "kth smallest element is larger than upper bounds"
|
||||
*>
|
||||
fn ElementType? qselect(ListType list, isz low, isz high, isz k, CmpFn cmp, Context context)
|
||||
macro ElementType? qselect(ListType list, isz low, isz high, isz k, CmpFn cmp = ..., Context context = ...)
|
||||
{
|
||||
if (low >= 0 && high >= 0 && low < high)
|
||||
{
|
||||
@@ -108,7 +114,7 @@ fn ElementType? qselect(ListType list, isz low, isz high, isz k, CmpFn cmp, Cont
|
||||
usz max_retries = 64;
|
||||
while (l <= h && max_retries--)
|
||||
{
|
||||
pivot = @partition(list, l, h, cmp, context);
|
||||
pivot = @partition(list, l, h, ...cmp, ...context);
|
||||
if (k == pivot) return list_get(list, k);
|
||||
if (k < pivot)
|
||||
{
|
||||
@@ -123,10 +129,10 @@ fn ElementType? qselect(ListType list, isz low, isz high, isz k, CmpFn cmp, Cont
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
macro isz @partition(ListType list, isz l, isz h, CmpFn cmp, Context context)
|
||||
macro isz @partition(ListType list, isz l, isz h, CmpFn cmp = ..., Context context = ...)
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(context);
|
||||
var $has_cmp = $defined(cmp);
|
||||
var $has_context = $defined(context);
|
||||
var $cmp_by_value = $has_cmp &&& $defined($typefrom(CmpFn.paramsof[0].type) v = list_get(list, 0));
|
||||
|
||||
ElementType pivot = list_get(list, l);
|
||||
|
||||
@@ -39,94 +39,102 @@ macro bool @is_any_sortable(#list) @const
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro bool @is_valid_context(#cmp, #context)
|
||||
macro bool @is_valid_context(#cmp = ..., #context = ...)
|
||||
{
|
||||
return @is_valid_macro_slot(#cmp) || @is_empty_macro_slot(#context);
|
||||
return $defined(#cmp) || !$defined(#context);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@require @list_is_by_ref(#list) : "Expected the list to be passed by ref or be a slice"
|
||||
*>
|
||||
macro bool @is_valid_cmp_fn(#cmp, #list, #context) @const
|
||||
macro bool @is_valid_cmp_fn(#cmp = ..., #list = ..., #context = ...) @const
|
||||
{
|
||||
var $Type = $typeof(#cmp);
|
||||
var $no_context = @is_empty_macro_slot(#context);
|
||||
$switch:
|
||||
$case @is_empty_macro_slot(#cmp): return true;
|
||||
$case $Type.kindof != FUNC ||| $Type.returns.kindof != SIGNED_INT: return false;
|
||||
$default:
|
||||
$if $kindof(#list) == SLICE:
|
||||
$switch:
|
||||
$case $defined(#cmp((#list)[0], (#list)[0], #context)): return true;
|
||||
$case $defined(#cmp((#list)[0], (#list)[0])): return $no_context;
|
||||
$case $defined(#cmp(&(#list)[0], &(#list)[0], #context)): return true;
|
||||
$case $defined(#cmp(&(#list)[0], &(#list)[0])): return $no_context;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$else
|
||||
$switch:
|
||||
$case $defined(#cmp((*#list)[0], (*#list)[0], #context)): return true;
|
||||
$case $defined(#cmp((*#list)[0], (*#list)[0])): return $no_context;
|
||||
$case $defined(#cmp(&(*#list)[0], &(*#list)[0], #context)): return true;
|
||||
$case $defined(#cmp(&(*#list)[0], &(*#list)[0])): return $no_context;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$endif
|
||||
$endswitch
|
||||
$if !$defined(#cmp):
|
||||
return true;
|
||||
$else
|
||||
var $Type = $typeof(#cmp);
|
||||
var $no_context = !$defined(#context);
|
||||
$switch:
|
||||
$case $Type.kindof != FUNC ||| $Type.returns.kindof != SIGNED_INT: return false;
|
||||
$default:
|
||||
$if $kindof(#list) == SLICE:
|
||||
$switch:
|
||||
$case $defined(#cmp((#list)[0], (#list)[0], #context)): return true;
|
||||
$case $defined(#cmp((#list)[0], (#list)[0])): return $no_context;
|
||||
$case $defined(#cmp(&(#list)[0], &(#list)[0], #context)): return true;
|
||||
$case $defined(#cmp(&(#list)[0], &(#list)[0])): return $no_context;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$else
|
||||
$switch:
|
||||
$case $defined(#cmp((*#list)[0], (*#list)[0], #context)): return true;
|
||||
$case $defined(#cmp((*#list)[0], (*#list)[0])): return $no_context;
|
||||
$case $defined(#cmp(&(*#list)[0], &(*#list)[0], #context)): return true;
|
||||
$case $defined(#cmp(&(*#list)[0], &(*#list)[0])): return $no_context;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$endif
|
||||
$endswitch
|
||||
$endif
|
||||
}
|
||||
|
||||
macro bool @is_any_valid_cmp_fn(#cmp, #list, #context) @const
|
||||
macro bool @is_any_valid_cmp_fn(#cmp = ..., #list = ..., #context = ...) @const
|
||||
{
|
||||
var $Type = $typeof(#cmp);
|
||||
var $no_context = @is_empty_macro_slot(#context);
|
||||
$switch:
|
||||
$case @is_empty_macro_slot(#cmp): return true;
|
||||
$case $Type.kindof != FUNC ||| $Type.returns.kindof != SIGNED_INT: return false;
|
||||
$default:
|
||||
$if $kindof(#list) != POINTER:
|
||||
$switch:
|
||||
$case $defined(#cmp((#list)[0], (#list)[0], #context)): return true;
|
||||
$case $defined(#cmp((#list)[0], (#list)[0])): return $no_context;
|
||||
$case $defined(#cmp(&(#list)[0], &(#list)[0], #context)): return true;
|
||||
$case $defined(#cmp(&(#list)[0], &(#list)[0])): return $no_context;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$else
|
||||
$switch:
|
||||
$case $defined(#cmp((*#list)[0], (*#list)[0], #context)): return true;
|
||||
$case $defined(#cmp((*#list)[0], (*#list)[0])): return $no_context;
|
||||
$case $defined(#cmp(&(*#list)[0], &(*#list)[0], #context)): return true;
|
||||
$case $defined(#cmp(&(*#list)[0], &(*#list)[0])): return $no_context;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$endif
|
||||
$endswitch
|
||||
$if !$defined(#cmp):
|
||||
return true;
|
||||
$else
|
||||
var $Type = $typeof(#cmp);
|
||||
var $no_context = !$defined(#context);
|
||||
$switch:
|
||||
$case $Type.kindof != FUNC ||| $Type.returns.kindof != SIGNED_INT: return false;
|
||||
$default:
|
||||
$if $kindof(#list) != POINTER:
|
||||
$switch:
|
||||
$case $defined(#cmp((#list)[0], (#list)[0], #context)): return true;
|
||||
$case $defined(#cmp((#list)[0], (#list)[0])): return $no_context;
|
||||
$case $defined(#cmp(&(#list)[0], &(#list)[0], #context)): return true;
|
||||
$case $defined(#cmp(&(#list)[0], &(#list)[0])): return $no_context;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$else
|
||||
$switch:
|
||||
$case $defined(#cmp((*#list)[0], (*#list)[0], #context)): return true;
|
||||
$case $defined(#cmp((*#list)[0], (*#list)[0])): return $no_context;
|
||||
$case $defined(#cmp(&(*#list)[0], &(*#list)[0], #context)): return true;
|
||||
$case $defined(#cmp(&(*#list)[0], &(*#list)[0])): return $no_context;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$endif
|
||||
$endswitch
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require @list_is_by_ref(#list) : "Expected the list to be passed by ref or be a slice"
|
||||
*>
|
||||
macro bool @is_cmp_key_fn(#key_fn, #list) @const
|
||||
macro bool @is_cmp_key_fn(#key_fn = ..., #list = ...) @const
|
||||
{
|
||||
$switch:
|
||||
$case @is_empty_macro_slot(#key_fn): return true;
|
||||
$case $kindof(#key_fn) != FUNC: return false;
|
||||
$case $typeof(#key_fn).returns.kindof != UNSIGNED_INT: return false;
|
||||
$default:
|
||||
$if $kindof(#list) == SLICE:
|
||||
$switch:
|
||||
$case $defined(#key_fn((#list)[0])): return true;
|
||||
$case $defined(#key_fn(&&((#list)[0]))): return true;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$else
|
||||
$switch:
|
||||
$case $defined(#key_fn((*#list)[0])): return true;
|
||||
$case $defined(#key_fn(&&((*#list)[0]))): return true;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$endif
|
||||
$endswitch
|
||||
|
||||
$if !$defined(#key_fn):
|
||||
return true;
|
||||
$else
|
||||
$switch:
|
||||
$case $kindof(#key_fn) != FUNC: return false;
|
||||
$case $typeof(#key_fn).returns.kindof != UNSIGNED_INT: return false;
|
||||
$default:
|
||||
$if $kindof(#list) == SLICE:
|
||||
$switch:
|
||||
$case $defined(#key_fn((#list)[0])): return true;
|
||||
$case $defined(#key_fn(&&((#list)[0]))): return true;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$else
|
||||
$switch:
|
||||
$case $defined(#key_fn((*#list)[0])): return true;
|
||||
$case $defined(#key_fn(&&((*#list)[0]))): return true;
|
||||
$default: return false;
|
||||
$endswitch
|
||||
$endif
|
||||
$endswitch
|
||||
$endif
|
||||
}
|
||||
@@ -4,94 +4,50 @@ module std::sort;
|
||||
Returns true if list is sorted in either ascending or descending order.
|
||||
|
||||
@require @is_any_sortable(list) : "The list must be indexable and support .len or .len()"
|
||||
@require @is_any_valid_cmp_fn(cmp, list, ctx) : "Expected a comparison function which compares values"
|
||||
@require @is_valid_context(cmp, ctx) : "Expected a valid context"
|
||||
@require @is_any_valid_cmp_fn(#cmp: ...cmp, #list: list, #context: ...ctx) : "Expected a comparison function which compares values"
|
||||
@require @is_valid_context(...cmp, ...ctx) : "Expected a valid context"
|
||||
*>
|
||||
macro bool is_sorted(list, cmp = EMPTY_MACRO_SLOT, ctx = EMPTY_MACRO_SLOT) @builtin
|
||||
macro bool is_sorted(list, cmp = ..., ctx = ...) @builtin
|
||||
{
|
||||
$if ($kindof(list) != POINTER):
|
||||
var $Type = $typeof(list);
|
||||
usz len = lengthof(list);
|
||||
// When the context or cmp functions are not defined, we can simply use a dummy/default type.
|
||||
const LIST_IS_REF = $kindof(list) == POINTER;
|
||||
var $Type = !LIST_IS_REF ??? $typeof(list) : $typeof(*list);
|
||||
|
||||
usz len = !LIST_IS_REF ??? lengthof(list) : lengthof(*list);
|
||||
if (len <= 1) return true;
|
||||
var check_sort = fn bool($Type list, usz start, usz end, $typeof(cmp) cmp, $typeof(ctx) ctx)
|
||||
|
||||
bool is_sorted = false;
|
||||
if CHECK_SORT: (true)
|
||||
{
|
||||
usz i;
|
||||
usz i = 0;
|
||||
int sort_order;
|
||||
|
||||
// determine sort order (ascending or descending)
|
||||
for (i = start; i < end && sort_order == 0; i++)
|
||||
for (; i < (len - 1) && sort_order == 0; i++)
|
||||
{
|
||||
sort_order = @sort_cmp_slice(list, i, cmp, ctx);
|
||||
sort_order = @sort_cmp(list, i, ...cmp, ...ctx);
|
||||
}
|
||||
|
||||
// no sort order found, all elements are the same, consider list sorted
|
||||
if (sort_order == 0) return true;
|
||||
|
||||
if (sort_order == 0)
|
||||
{
|
||||
is_sorted = true;
|
||||
break CHECK_SORT;
|
||||
}
|
||||
// compare adjacent elements to the sort order
|
||||
for (; i < end; i++)
|
||||
for (; i < (len - 1); i++)
|
||||
{
|
||||
if (sort_order * @sort_cmp_slice(list, i, cmp, ctx) < 0) return false;
|
||||
if (sort_order * @sort_cmp(list, i, ...cmp, ...ctx) < 0) break CHECK_SORT;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
is_sorted = true;
|
||||
}
|
||||
|
||||
return check_sort(list, 0, len - 1, cmp, ctx);
|
||||
$else
|
||||
var $Type = $typeof(*list);
|
||||
usz len = lengthof(*list);
|
||||
if (len <= 1) return true;
|
||||
var check_sort = fn bool($Type* list, usz start, usz end, $typeof(cmp) cmp, $typeof(ctx) ctx)
|
||||
{
|
||||
usz i;
|
||||
int sort_order;
|
||||
|
||||
// determine sort order (ascending or descending)
|
||||
for (i = start; i < end && sort_order == 0; i++)
|
||||
{
|
||||
sort_order = @sort_cmp(list, i, cmp, ctx);
|
||||
}
|
||||
|
||||
// no sort order found, all elements are the same, consider list sorted
|
||||
if (sort_order == 0) return true;
|
||||
|
||||
// compare adjacent elements to the sort order
|
||||
for (; i < end; i++)
|
||||
{
|
||||
if (sort_order * @sort_cmp(list, i, cmp, ctx) < 0) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return check_sort(list, 0, len - 1, cmp, ctx);
|
||||
$endif
|
||||
return is_sorted;
|
||||
}
|
||||
|
||||
macro int @sort_cmp(list, pos, cmp, ctx) @local
|
||||
macro int @sort_cmp(list_maybe_ref, pos, cmp = ..., ctx = ...) @local
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(ctx);
|
||||
var $cmp_by_value = $has_cmp &&& $defined($typefrom($typeof(cmp).paramsof[0].type) v = (*list)[0]);
|
||||
|
||||
var a = (*list)[pos];
|
||||
var b = (*list)[pos+1];
|
||||
|
||||
$switch:
|
||||
$case $cmp_by_value && $has_context:
|
||||
return cmp(a, b);
|
||||
$case $cmp_by_value:
|
||||
return cmp(a, b);
|
||||
$case $has_cmp && $has_context:
|
||||
return cmp(&a, &b, ctx);
|
||||
$case $has_cmp:
|
||||
return cmp(&a, &b);
|
||||
$default:
|
||||
return compare_to(a,b);
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro int @sort_cmp_slice(list, pos, cmp, ctx) @local
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(ctx);
|
||||
var list = $kindof(list_maybe_ref) != POINTER ??? list_maybe_ref : *list_maybe_ref;
|
||||
var $has_cmp = $defined(cmp);
|
||||
var $has_context = $defined(ctx);
|
||||
var $cmp_by_value = $has_cmp &&& $defined($typefrom($typeof(cmp).paramsof[0].type) v = list[0]);
|
||||
|
||||
var a = list[pos];
|
||||
|
||||
Reference in New Issue
Block a user