Skip to content

Commit

Permalink
value: Add compare_total()
Browse files Browse the repository at this point in the history
  • Loading branch information
lhmouse committed Oct 20, 2023
1 parent 4ee680f commit a416473
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 161 deletions.
77 changes: 26 additions & 51 deletions asteria/library/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ do_find_opt(Global_Context& global, Reference_Stack& stack,
}
else {
// `target` is a plain value.
result = it->compare(target) == compare_equal;
result = it->compare_partial(target) == compare_equal;
}
if(result == match)
return ::std::move(it);
Expand All @@ -86,12 +86,12 @@ do_find_opt(Global_Context& global, Reference_Stack& stack,
}

Compare
do_compare(Global_Context& global, Reference_Stack& stack,
const optV_function& kcomp, const Value& lhs, const Value& rhs)
do_compare_total(Global_Context& global, Reference_Stack& stack,
const optV_function& kcomp, const Value& lhs, const Value& rhs)
{
// Use the builtin 3-way comparison operator if no comparator is provided.
if(ROCKET_EXPECT(!kcomp))
return lhs.compare(rhs);
return lhs.compare_total(rhs);

// Set up arguments for the user-defined comparator.
stack.clear();
Expand All @@ -102,7 +102,7 @@ do_compare(Global_Context& global, Reference_Stack& stack,
Reference self;
self.set_temporary(nullopt);
kcomp.invoke(self, global, ::std::move(stack));
return self.dereference_readonly().compare(V_integer(0));
return self.dereference_readonly().compare_total(V_integer(0));
}

template<typename IterT>
Expand All @@ -120,11 +120,7 @@ do_bsearch(Global_Context& global, Reference_Stack& stack, IterT begin, IterT en

// Compare `target` to the element in the middle.
auto mpos = bpos + (dist >> 1);
auto cmp = do_compare(global, stack, kcomp, target, *mpos);
if(cmp == compare_unordered)
ASTERIA_THROW_RUNTIME_ERROR((
"Unordered elements (operands were `$1` and `$2`)"),
target, *mpos);
auto cmp = do_compare_total(global, stack, kcomp, target, *mpos);

if(cmp == compare_equal)
return { ::std::move(mpos), true };
Expand All @@ -151,11 +147,7 @@ do_bound(Global_Context& global, Reference_Stack& stack, IterT begin, IterT end,

// Compare `target` to the element in the middle.
auto mpos = bpos + dist / 2;
auto cmp = do_compare(global, stack, kcomp, target, *mpos);
if(cmp == compare_unordered)
ASTERIA_THROW_RUNTIME_ERROR((
"Unordered elements (operands were `$1` and `$2`)"),
target, *mpos);
auto cmp = do_compare_total(global, stack, kcomp, target, *mpos);

if(pred(cmp))
epos = mpos;
Expand Down Expand Up @@ -393,24 +385,19 @@ std_array_exclude_not(Global_Context& global, V_array data, V_integer from, optV
V_boolean
std_array_is_sorted(Global_Context& global, V_array data, optV_function comparator)
{
if(data.size() == 0)
auto pos = data.begin();
if(pos == data.end())
return true;

// The first element shall not be unordered with itself.
Reference_Stack stack;
auto comp = do_compare(global, stack, comparator, data.front(), data.front());
if(comp != compare_equal)
if(do_compare_total(global, stack, comparator, *pos, *pos) != compare_equal)
return false;

if(data.size() <= 1)
return true;

// Check remaining elements.
for(size_t k = 1; k < data.size(); ++k) {
comp = do_compare(global, stack, comparator, data.at(k - 1), data.at(k));
if((comp != compare_less) && (comp != compare_equal))
while(++pos != data.end())
if(do_compare_total(global, stack, comparator, pos[-1], pos[0]) == compare_greater)
return false;
}

return true;
}

Expand Down Expand Up @@ -462,7 +449,7 @@ std_array_sort(Global_Context& global, V_array data, optV_function comparator)
// Merge blocks of exponential sizes.
Reference_Stack stack;
auto compare = [&](const Value& lhs, const Value& rhs)
{ return do_compare(global, stack, comparator, lhs, rhs); };
{ return do_compare_total(global, stack, comparator, lhs, rhs); };

V_array temp(data.size());
ptrdiff_t bsize = 1;
Expand All @@ -484,7 +471,7 @@ std_array_sortu(Global_Context& global, V_array data, optV_function comparator)
// Merge blocks of exponential sizes.
Reference_Stack stack;
auto compare = [&](const Value& lhs, const Value& rhs)
{ return do_compare(global, stack, comparator, lhs, rhs); };
{ return do_compare_total(global, stack, comparator, lhs, rhs); };

V_array temp(data.size());
ptrdiff_t bsize = 1;
Expand Down Expand Up @@ -518,8 +505,7 @@ std_array_ksort(Global_Context& global, V_object object, optV_function comparato
// Merge blocks of exponential sizes. Keys are known to be unique.
Reference_Stack stack;
auto compare = [&](const Value& lhs, const Value& rhs)
{ return do_compare(global, stack, comparator,
lhs.as_array().at(0), rhs.as_array().at(0)); };
{ return do_compare_total(global, stack, comparator, lhs.as_array().at(0), rhs.as_array().at(0)); };

V_array temp(data.size());
ptrdiff_t bsize = 1;
Expand All @@ -534,39 +520,28 @@ std_array_ksort(Global_Context& global, V_object object, optV_function comparato
Value
std_array_max_of(Global_Context& global, V_array data, optV_function comparator)
{
// Return `null` if `data` is empty.
auto qmax = data.begin();
if(qmax == data.end())
return nullopt;

// Compare `*qmax` with the other elements, ignoring unordered elements.
Value result;
Reference_Stack stack;
for(auto it = qmax + 1; it != data.end(); ++it)
if(do_compare(global, stack, comparator, *qmax, *it) == compare_less)
qmax = it;
return *qmax;
for(const auto& r : data)
if(result.is_null() || (!r.is_null() && do_compare_total(global, stack, comparator, result, r) == compare_less))
result = r;
return result;
}

Value
std_array_min_of(Global_Context& global, V_array data, optV_function comparator)
{
// Return `null` if `data` is empty.
auto qmin = data.begin();
if(qmin == data.end())
return nullopt;

// Compare `*qmin` with the other elements, ignoring unordered elements.
Value result;
Reference_Stack stack;
for(auto it = qmin + 1; it != data.end(); ++it)
if(do_compare(global, stack, comparator, *qmin, *it) == compare_greater)
qmin = it;
return *qmin;
for(const auto& r : data)
if(result.is_null() || (!r.is_null() && do_compare_total(global, stack, comparator, result, r) == compare_greater))
result = r;
return result;
}

V_array
std_array_reverse(V_array data)
{
// This is an easy matter, isn't it?
return V_array(data.move_rbegin(), data.move_rend());
}

Expand Down
46 changes: 10 additions & 36 deletions asteria/library/numeric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,47 +300,21 @@ std_numeric_is_nan(V_real value)
Value
std_numeric_max(cow_vector<Value> values)
{
Value res;
for(const auto& val : values) {
if(val.is_null())
continue;

if(!res.is_null()) {
auto cmp = res.compare(val);
if(cmp == compare_unordered)
ASTERIA_THROW_RUNTIME_ERROR((
"Values not comparable (operands were `$1` and `$2`)"),
cmp, val);

if(cmp != compare_less)
continue;
}
res = val;
}
return res;
Value result;
for(const auto& r : values)
if(result.is_null() || (result.compare_partial(r) == compare_less))
result = r;
return result;
}

Value
std_numeric_min(cow_vector<Value> values)
{
Value res;
for(const auto& val : values) {
if(val.is_null())
continue;

if(!res.is_null()) {
auto cmp = res.compare(val);
if(cmp == compare_unordered)
ASTERIA_THROW_RUNTIME_ERROR((
"Values not comparable (operands were `$1` and `$2`)"),
cmp, val);

if(cmp != compare_greater)
continue;
}
res = val;
}
return res;
Value result;
for(const auto& r : values)
if(result.is_null() || (result.compare_partial(r) == compare_greater))
result = r;
return result;
}

V_integer
Expand Down
42 changes: 9 additions & 33 deletions asteria/runtime/air_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ struct Traits_switch_statement
// Evaluate the operand and check whether it equals `cond`.
AIR_Status status = sp.queues_labels[i].execute(ctx);
ROCKET_ASSERT(status == air_status_next);
if(ctx.stack().top().dereference_readonly().compare(cond) == compare_equal) {
if(ctx.stack().top().dereference_readonly().compare_partial(cond) == compare_equal) {
target_index = i;
break;
}
Expand Down Expand Up @@ -2383,7 +2383,7 @@ struct Traits_apply_xop_cmp_eq

// Check whether the two operands are equal. Unordered values are
// considered to compare unequal.
lhs = lhs.compare(rhs) == compare_equal;
lhs = lhs.compare_partial(rhs) == compare_equal;
return air_status_next;
}
};
Expand Down Expand Up @@ -2418,7 +2418,7 @@ struct Traits_apply_xop_cmp_ne

// Check whether the two operands are not equal. Unordered values
// are considered to compare unequal.
lhs = lhs.compare(rhs) != compare_equal;
lhs = lhs.compare_partial(rhs) != compare_equal;
return air_status_next;
}
};
Expand Down Expand Up @@ -2453,13 +2453,7 @@ struct Traits_apply_xop_cmp_lt

// Check whether the LHS operand is less than the RHS operand. If
// they are unordered, an exception shall be thrown.
Compare cmp = lhs.compare(rhs);
if(cmp == compare_unordered)
ASTERIA_THROW_RUNTIME_ERROR((
"Values not comparable (operands were `$1` and `$2`)"),
lhs, rhs);

lhs = cmp == compare_less;
lhs = lhs.compare_total(rhs) == compare_less;
return air_status_next;
}
};
Expand Down Expand Up @@ -2494,13 +2488,7 @@ struct Traits_apply_xop_cmp_gt

// Check whether the LHS operand is greater than the RHS operand. If
// they are unordered, an exception shall be thrown.
Compare cmp = lhs.compare(rhs);
if(cmp == compare_unordered)
ASTERIA_THROW_RUNTIME_ERROR((
"Values not comparable (operands were `$1` and `$2`)"),
lhs, rhs);

lhs = cmp == compare_greater;
lhs = lhs.compare_total(rhs) == compare_greater;
return air_status_next;
}
};
Expand Down Expand Up @@ -2535,13 +2523,7 @@ struct Traits_apply_xop_cmp_lte

// Check whether the LHS operand is less than or equal to the RHS
// operand. If they are unordered, an exception shall be thrown.
Compare cmp = lhs.compare(rhs);
if(cmp == compare_unordered)
ASTERIA_THROW_RUNTIME_ERROR((
"Values not comparable (operands were `$1` and `$2`)"),
lhs, rhs);

lhs = cmp != compare_greater;
lhs = lhs.compare_total(rhs) != compare_greater;
return air_status_next;
}
};
Expand Down Expand Up @@ -2576,13 +2558,7 @@ struct Traits_apply_xop_cmp_gte

// Check whether the LHS operand is greater than or equal to the RHS
// operand. If they are unordered, an exception shall be thrown.
Compare cmp = lhs.compare(rhs);
if(cmp == compare_unordered)
ASTERIA_THROW_RUNTIME_ERROR((
"Values not comparable (operands were `$1` and `$2`)"),
lhs, rhs);

lhs = cmp != compare_less;
lhs = lhs.compare_total(rhs) != compare_less;
return air_status_next;
}
};
Expand Down Expand Up @@ -2616,7 +2592,7 @@ struct Traits_apply_xop_cmp_3way
auto& lhs = up.b0 ? top.dereference_mutable() : top.dereference_copy();

// Perform 3-way comparison of both operands.
Compare cmp = lhs.compare(rhs);
Compare cmp = lhs.compare_partial(rhs);
if(cmp == compare_unordered)
lhs = sref("[unordered]");
else {
Expand Down Expand Up @@ -2658,7 +2634,7 @@ struct Traits_apply_xop_cmp_un
auto& lhs = up.b0 ? top.dereference_mutable() : top.dereference_copy();

// Check whether the two operands are unordered.
lhs = lhs.compare(rhs) == compare_unordered;
lhs = lhs.compare_partial(rhs) == compare_unordered;
return air_status_next;
}
};
Expand Down
11 changes: 10 additions & 1 deletion asteria/value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,18 @@ do_throw_type_mismatch(const char* desc) const
desc, describe_type(this->type()));
}

void
Value::
do_throw_uncomparable_with(const Value& other) const
{
ASTERIA_THROW((
"Values not comparable (operands were `$1` and `$2`)"),
*this, other);
}

Compare
Value::
compare(const Value& other) const noexcept
compare_partial(const Value& other) const
{
// Expand recursion by hand with a stack.
auto qval = this;
Expand Down
20 changes: 17 additions & 3 deletions asteria/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ class Value
void
do_throw_type_mismatch(const char* desc) const;

[[noreturn]]
void
do_throw_uncomparable_with(const Value& other) const;

public:
~Value()
{
Expand Down Expand Up @@ -312,7 +316,7 @@ class Value
void
collect_variables(Variable_HashMap& staged, Variable_HashMap& temp) const
{
if(ROCKET_UNEXPECT(this->type() >= type_opaque))
if(this->type() >= type_opaque)
this->do_collect_variables_slow(staged, temp);
}

Expand Down Expand Up @@ -344,9 +348,19 @@ class Value
}
}

// This performs the builtin comparison with another value.
// These perform the builtin comparison.
Compare
compare_partial(const Value& other) const;

Compare
compare(const Value& other) const noexcept;
compare_total(const Value& other) const
{
auto cmp = this->compare_partial(other);
if(cmp != compare_unordered)
return cmp;

this->do_throw_uncomparable_with(other);
}

// These are miscellaneous interfaces for debugging.
tinyfmt&
Expand Down
Loading

0 comments on commit a416473

Please sign in to comment.