Implement comparison operators

This commit is contained in:
Laria 2022-11-20 13:47:38 +01:00
parent 3bbd0e79a1
commit f9878b43d8
8 changed files with 149 additions and 0 deletions

View file

@ -69,6 +69,7 @@ functionaltest("chained-assignments")
functionaltest("dictionary-assignments")
functionaltest("variadic-functions")
functionaltest("predicate")
functionaltest("compare")
install(TARGETS apfl DESTINATION lib)
install(TARGETS apfl-bin DESTINATION bin)

View file

@ -698,6 +698,8 @@ bool apfl_is_truthy(apfl_ctx, apfl_stackidx);
apfl_number apfl_get_number(apfl_ctx, apfl_stackidx);
// Pops two values from the stack and returns whether they are equal.
bool apfl_eq(apfl_ctx, apfl_stackidx, apfl_stackidx);
// Pops two values from the stack and compares them (a < b: -1; a = b: 0; a > b: 1)
int apfl_cmp(apfl_ctx, apfl_stackidx a, apfl_stackidx b);
// Push a C function onto the stack with nslots slots (initialized to nil)
void apfl_push_cfunc(apfl_ctx, apfl_cfunc, size_t nslots);
@ -746,6 +748,8 @@ struct apfl_messages {
const char *value_doesnt_match;
const char *invalid_matcher_state;
const char *no_matching_subfunction;
const char *uncomparable;
const char *incompatible_types;
};
extern const struct apfl_messages apfl_messages;

View file

@ -1251,6 +1251,32 @@ apfl_eq(apfl_ctx ctx, apfl_stackidx _a, apfl_stackidx _b)
return eq;
}
int
apfl_cmp(apfl_ctx ctx, apfl_stackidx _a, apfl_stackidx _b)
{
struct apfl_value a = apfl_stack_must_get(ctx, _a);
struct apfl_value b = apfl_stack_must_get(ctx, _b);
enum comparison_result result = apfl_value_cmp(a, b);
assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){_a, _b}));
switch (result) {
case CMP_LT:
return -1;
case CMP_EQ:
return 0;
case CMP_GT:
return 1;
case CMP_UNCOMPARABLE:
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.uncomparable);
case CMP_INCOMPATIBLE_TYPES:
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.incompatible_types);
}
assert(false);
return 0;
}
static struct scope *
closure_scope_for_func_inner(apfl_ctx ctx, struct scopes scopes)
{

View file

@ -0,0 +1,22 @@
===== script =====
compare = { ~args ->
print ~args "==>" (> ~args) (>= ~args) (< ~args) (<= ~args)
}
compare 1 2
compare 1 1
compare "foo" "bar"
compare 1 2 3
compare 1 3 2
compare nil nil
compare true false
===== output =====
1 2 ==> false false true true
1 1 ==> false true false true
foo bar ==> true true false false
1 2 3 ==> false false true true
1 3 2 ==> false false false false
nil nil ==> false true false true
true false ==> true true false false

View file

@ -81,6 +81,32 @@ impl_eq(apfl_ctx ctx)
apfl_push_bool(ctx, true);
}
#define IMPLEMENT_COMPARISON(impl_name, name, cmp) \
static void \
impl_name(apfl_ctx ctx) \
{ \
size_t len = apfl_len(ctx, 0); \
if (len < 2) { \
apfl_raise_const_error(ctx, APFL_RESULT_ERR, name " needs at least 2 arguments"); \
} \
\
for (size_t i = 0; i < len-1; i++) { \
apfl_get_list_member_by_index(ctx, 0, i); \
apfl_get_list_member_by_index(ctx, 0, i+1); \
if (!(apfl_cmp(ctx, -2, -1) cmp 0)) { \
apfl_push_bool(ctx, false); \
return; \
} \
} \
\
apfl_push_bool(ctx, true); \
} \
IMPLEMENT_COMPARISON(impl_gt, ">", >)
IMPLEMENT_COMPARISON(impl_lt, "<", <)
IMPLEMENT_COMPARISON(impl_ge, ">=", >=)
IMPLEMENT_COMPARISON(impl_le, "<=", <=)
#define IMPL_MATH_OP(name, op) \
static apfl_number \
name(apfl_ctx ctx, apfl_number a, apfl_number b) \
@ -332,6 +358,10 @@ impl_gc(apfl_ctx ctx)
static const struct global_def globals[] = {
{"if", impl_if},
{"==", impl_eq},
{">", impl_gt},
{"<", impl_lt},
{">=", impl_ge},
{"<=", impl_le},
{"+", impl_plus},
{"-", impl_minus},
{"*", impl_mult},

View file

@ -20,4 +20,6 @@ const struct apfl_messages apfl_messages = {
.value_doesnt_match = "Value does not match",
.invalid_matcher_state = "Matcher is in invalid state",
.no_matching_subfunction = "No matching subfunction",
.uncomparable = "Value is not comparable",
.incompatible_types = "Incompatible types",
};

View file

@ -367,6 +367,60 @@ apfl_value_eq(const struct apfl_value a, const struct apfl_value b)
return false;
}
#define CMP(a, b) (((a) == (b)) ? CMP_EQ : (((a) < (b)) ? CMP_LT : CMP_GT))
enum comparison_result
apfl_value_cmp(const struct apfl_value a, const struct apfl_value b)
{
switch (a.type) {
case VALUE_NIL:
if (b.type != VALUE_NIL) {
return CMP_INCOMPATIBLE_TYPES;
}
return CMP_EQ;
case VALUE_BOOLEAN:
if (b.type != VALUE_BOOLEAN) {
return CMP_INCOMPATIBLE_TYPES;
}
return CMP(a.boolean ? 1 : 0, b.boolean ? 1 : 0);
case VALUE_NUMBER:
if (b.type != VALUE_NUMBER) {
return CMP_INCOMPATIBLE_TYPES;
}
return CMP(a.number, b.number);
case VALUE_STRING:
case VALUE_CONST_STRING:
if (b.type != VALUE_STRING && b.type != VALUE_CONST_STRING) {
return CMP_INCOMPATIBLE_TYPES;
}
return CMP(apfl_string_cmp(as_string_view(a), as_string_view(b)), 0);
case VALUE_LIST:
if (b.type != VALUE_LIST) {
return CMP_INCOMPATIBLE_TYPES;
}
return CMP_UNCOMPARABLE;
case VALUE_DICT:
if (b.type != VALUE_DICT) {
return CMP_INCOMPATIBLE_TYPES;
}
return CMP_UNCOMPARABLE;
case VALUE_FUNC:
case VALUE_CFUNC:
if (b.type != VALUE_FUNC && b.type != VALUE_CFUNC) {
return CMP_INCOMPATIBLE_TYPES;
}
return CMP_UNCOMPARABLE;
case VALUE_USERDATA:
if (b.type != VALUE_USERDATA) {
return CMP_INCOMPATIBLE_TYPES;
}
return CMP_UNCOMPARABLE;
}
assert(false);
return CMP_INCOMPATIBLE_TYPES;
}
struct list_header *
apfl_list_new(struct gc *gc, size_t initial_cap)
{

View file

@ -86,6 +86,16 @@ bool apfl_value_format(struct apfl_value, struct apfl_format_writer);
bool apfl_value_print(struct apfl_value, struct apfl_format_writer);
apfl_hash apfl_value_hash(const struct apfl_value);
enum comparison_result {
CMP_LT,
CMP_EQ,
CMP_GT,
CMP_UNCOMPARABLE,
CMP_INCOMPATIBLE_TYPES,
};
enum comparison_result apfl_value_cmp(const struct apfl_value a, const struct apfl_value b);
enum get_item_result {
GET_ITEM_OK,
GET_ITEM_KEY_DOESNT_EXIST,