Implement comparison operators
This commit is contained in:
parent
3bbd0e79a1
commit
f9878b43d8
8 changed files with 149 additions and 0 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
22
src/functional-tests/compare.at
Normal file
22
src/functional-tests/compare.at
Normal 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
|
||||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
};
|
||||
|
|
|
|||
54
src/value.c
54
src/value.c
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
10
src/value.h
10
src/value.h
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue