comparison operators, acceptance tests.

main
bog 2024-03-30 17:24:52 +01:00
parent 0bb1512fc4
commit 1c5ad117d8
8 changed files with 261 additions and 19 deletions

24
features/cmp.mk Normal file
View File

@ -0,0 +1,24 @@
(assert-eqv! true (< 1 2))
(assert-eqv! false (< 2 2))
(assert-eqv! false (< 3 2))
(assert-eqv! true (<= 1 2))
(assert-eqv! true (<= 2 2))
(assert-eqv! false (<= 3 2))
(assert-eqv! true (> 3 2))
(assert-eqv! false (> 3 3))
(assert-eqv! false (> 3 21))
(assert-eqv! true (>= 3 2))
(assert-eqv! true (>= 3 3))
(assert-eqv! false (>= 3 21))
(assert-eqv! false (eqv? 3 2))
(assert-eqv! true (eqv? 3 3))
(assert-eqv! false (eqv? [2 4] [2 5]))
(assert-eqv! true (eqv? [2 4] [2 4]))
(assert-eqv! true (nev? [2 4] [2 5]))
(assert-eqv! false (nev? [2 4] [2 4]))

30
features/run.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
TOTAL=0
FAILURES=0
for file in $(find . -name "*.mk")
do
OUTPUT=$(moka $file 2>&1)
STATUS=$?
echo -en "\e[35m$file\e[0m ... "
if [ $STATUS -eq 0 ]
then
echo -e "\e[32mpassed\e[0m"
else
echo -e "\e[31mfailed\e[0m"
echo "$OUTPUT"
FAILURES=$(($FAILURES + 1))
fi
TOTAL=$(($TOTAL + 1))
done
if [ $FAILURES -eq 0 ]
then
echo -e "\e[32m--- All tests passed ---\e[32m"
else
echo -e "\e[31m--- $FAILURES tests failed ---\e[32m"
fi

View File

@ -7,10 +7,17 @@
void register_builtins(struct moka* moka) void register_builtins(struct moka* moka)
{ {
assert(moka); assert(moka);
moka_decl_native(moka, "println", mk_println); moka_decl_native(moka, "println", mk_println, -1);
moka_decl_native(moka, "define", mk_define); moka_decl_native(moka, "define", mk_define, 2);
moka_decl_native(moka, "array", mk_array); moka_decl_native(moka, "array", mk_array, -1);
moka_decl_native(moka, "assert-eqv!", mk_assert_eqv_mut); moka_decl_native(moka, "assert-eqv!", mk_assert_eqv_mut, 2);
moka_decl_native(moka, "<", mk_lt, 2);
moka_decl_native(moka, "<=", mk_le, 2);
moka_decl_native(moka, ">", mk_gt, 2);
moka_decl_native(moka, ">=", mk_ge, 2);
moka_decl_native(moka, "eqv?", mk_is_eqv, 2);
moka_decl_native(moka, "nev?", mk_is_nev, 2);
} }
MOKA mk_println(struct moka* moka, struct vec* args) MOKA mk_println(struct moka* moka, struct vec* args)
@ -93,3 +100,89 @@ MOKA mk_assert_eqv_mut(struct moka* moka, struct vec* args)
return moka_push_bool(moka, true, line); return moka_push_bool(moka, true, line);
} }
// Comparisons
// ===========
MOKA mk_lt(struct moka* moka, struct vec* args)
{
assert(moka);
assert(args);
MOKA lhs = MK_EVAL((MOKA)args->data[0]);
MOKA rhs = MK_EVAL((MOKA)args->data[1]);
return moka_push_bool(
moka, moka_is_lt(moka, lhs, rhs),
moka_line(moka, lhs)
);
}
MOKA mk_le(struct moka* moka, struct vec* args)
{
assert(moka);
assert(args);
MOKA lhs = MK_EVAL((MOKA)args->data[0]);
MOKA rhs = MK_EVAL((MOKA)args->data[1]);
return moka_push_bool(
moka, !moka_is_gt(moka, lhs, rhs),
moka_line(moka, lhs)
);
}
MOKA mk_gt(struct moka* moka, struct vec* args)
{
assert(moka);
assert(args);
MOKA lhs = MK_EVAL((MOKA)args->data[0]);
MOKA rhs = MK_EVAL((MOKA)args->data[1]);
return moka_push_bool(
moka, moka_is_gt(moka, lhs, rhs),
moka_line(moka, lhs)
);
}
MOKA mk_ge(struct moka* moka, struct vec* args)
{
assert(moka);
assert(args);
MOKA lhs = MK_EVAL((MOKA)args->data[0]);
MOKA rhs = MK_EVAL((MOKA)args->data[1]);
return moka_push_bool(
moka, !moka_is_lt(moka, lhs, rhs),
moka_line(moka, lhs)
);
}
MOKA mk_is_eqv(struct moka* moka, struct vec* args)
{
assert(moka);
assert(args);
MOKA lhs = MK_EVAL((MOKA)args->data[0]);
MOKA rhs = MK_EVAL((MOKA)args->data[1]);
return moka_push_bool(
moka, moka_is_eqv(moka, lhs, rhs),
moka_line(moka, lhs)
);
}
MOKA mk_is_nev(struct moka* moka, struct vec* args)
{
assert(moka);
assert(args);
MOKA lhs = MK_EVAL((MOKA)args->data[0]);
MOKA rhs = MK_EVAL((MOKA)args->data[1]);
return moka_push_bool(
moka, !moka_is_eqv(moka, lhs, rhs),
moka_line(moka, lhs)
);
}

View File

@ -13,4 +13,13 @@ MOKA mk_define(struct moka* moka, struct vec* args);
MOKA mk_array(struct moka* moka, struct vec* args); MOKA mk_array(struct moka* moka, struct vec* args);
MOKA mk_assert_eqv_mut(struct moka* moka, struct vec* args); MOKA mk_assert_eqv_mut(struct moka* moka, struct vec* args);
// Comparisons
// ===========
MOKA mk_lt(struct moka* moka, struct vec* args);
MOKA mk_le(struct moka* moka, struct vec* args);
MOKA mk_gt(struct moka* moka, struct vec* args);
MOKA mk_ge(struct moka* moka, struct vec* args);
MOKA mk_is_eqv(struct moka* moka, struct vec* args);
MOKA mk_is_nev(struct moka* moka, struct vec* args);
#endif #endif

View File

@ -100,7 +100,8 @@ void moka_import_module(struct moka* self,
moka_decl_native( moka_decl_native(
self, self,
new_name, new_name,
val->data.native->fun val->data.native->fun,
val->data.native->arity
); );
} break; } break;
@ -181,10 +182,11 @@ void moka_decl_var(struct moka* self,
void moka_decl_native(struct moka* self, void moka_decl_native(struct moka* self,
char* name, char* name,
native_fun_t fun) native_fun_t fun,
int arity)
{ {
struct native* native = malloc(sizeof(struct native)); struct native* native = malloc(sizeof(struct native));
native_init(native, fun); native_init(native, fun, arity);
struct value* value = malloc(sizeof(struct value)); struct value* value = malloc(sizeof(struct value));
value_init_native(value, native, 0); value_init_native(value, native, 0);
@ -252,6 +254,60 @@ size_t moka_str(struct moka* self, MOKA value,
return value_str(val, buffer, size, self); return value_str(val, buffer, size, self);
} }
bool moka_is_lt(struct moka* self, MOKA mk_lhs, MOKA mk_rhs)
{
struct frame* frame = moka_frame(self);
struct value* lhs = frame->local_values.data[mk_lhs];
struct value* rhs = frame->local_values.data[mk_rhs];
if (lhs->type != rhs->type)
{
return false;
}
switch (lhs->type)
{
case TY_INT: {
return lhs->data.integer < rhs->data.integer;
} break;
case TY_FLOAT: {
return lhs->data.real < rhs->data.real;
} break;
default: {
fprintf(stderr, "cannot compare type <%s>\n",
TypeKindStr[lhs->type]);
abort();
} break;
}
}
bool moka_is_gt(struct moka* self, MOKA mk_lhs, MOKA mk_rhs)
{
struct frame* frame = moka_frame(self);
struct value* lhs = frame->local_values.data[mk_lhs];
struct value* rhs = frame->local_values.data[mk_rhs];
if (lhs->type != rhs->type)
{
return false;
}
switch (lhs->type)
{
case TY_INT: {
return lhs->data.integer > rhs->data.integer;
} break;
case TY_FLOAT: {
return lhs->data.real > rhs->data.real;
} break;
default: {
fprintf(stderr, "cannot compare type <%s>\n",
TypeKindStr[lhs->type]);
abort();
} break;
}
}
bool moka_is_eqv(struct moka* self, MOKA mk_lhs, MOKA mk_rhs) bool moka_is_eqv(struct moka* self, MOKA mk_lhs, MOKA mk_rhs)
{ {
struct frame* frame = moka_frame(self); struct frame* frame = moka_frame(self);
@ -390,10 +446,12 @@ MOKA moka_call(struct moka* self, int arg_count)
MOKA fun = moka_pop(self); MOKA fun = moka_pop(self);
struct vec args; struct vec args;
vec_init(&args); vec_init(&args);
int line = 0;
for (ssize_t i=0; i<arg_count; i++) for (ssize_t i=0; i<arg_count; i++)
{ {
MOKA arg = moka_pop(self); MOKA arg = moka_pop(self);
line = moka_line(self, arg);
vec_push(&args, (void*) arg); vec_push(&args, (void*) arg);
} }
@ -403,6 +461,19 @@ MOKA moka_call(struct moka* self, int arg_count)
assert(val->type == TY_NATIVE); assert(val->type == TY_NATIVE);
struct native* native = val->data.native; struct native* native = val->data.native;
if (native->arity > 0 && arg_count != native->arity)
{
status_push(self->status,
STATUS_ERROR,
line,
"<%d> arguments expected, got <%d>",
native->arity,
arg_count);
vec_free(&args);
return moka_push_bool(self, false, line);
}
(native->fun)(self, &args); (native->fun)(self, &args);
vec_free(&args); vec_free(&args);
@ -544,13 +615,16 @@ size_t moka_get_ref(struct moka* self, MOKA value)
return val->data.ref; return val->data.ref;
} }
MOKA moka_push_native(struct moka* self, native_fun_t value, int line) MOKA moka_push_native(struct moka* self,
native_fun_t value,
int arity,
int line)
{ {
assert(self); assert(self);
assert(value); assert(value);
struct native* native = malloc(sizeof(struct native)); struct native* native = malloc(sizeof(struct native));
native_init(native, value); native_init(native, value, arity);
struct value* val = malloc(sizeof(struct value)); struct value* val = malloc(sizeof(struct value));
value_init_native(val, native, line); value_init_native(val, native, line);

View File

@ -51,7 +51,8 @@ void moka_decl_var(struct moka* self,
void moka_decl_native(struct moka* self, void moka_decl_native(struct moka* self,
char* name, char* name,
native_fun_t fun); native_fun_t fun,
int arity);
struct frame* moka_frame(struct moka* self); struct frame* moka_frame(struct moka* self);
bool moka_has_top(struct moka* self); bool moka_has_top(struct moka* self);
@ -61,6 +62,9 @@ MOKA moka_pop(struct moka* self);
int moka_line(struct moka* self, MOKA value); int moka_line(struct moka* self, MOKA value);
size_t moka_str(struct moka* self, MOKA value, size_t moka_str(struct moka* self, MOKA value,
char* buffer, size_t size); char* buffer, size_t size);
bool moka_is_lt(struct moka* self, MOKA mk_lhs, MOKA mk_rhs);
bool moka_is_gt(struct moka* self, MOKA mk_lhs, MOKA mk_rhs);
bool moka_is_eqv(struct moka* self, MOKA mk_lhs, MOKA mk_rhs); bool moka_is_eqv(struct moka* self, MOKA mk_lhs, MOKA mk_rhs);
bool moka_is(struct moka* self, MOKA value, TypeKind type); bool moka_is(struct moka* self, MOKA value, TypeKind type);
@ -91,7 +95,10 @@ char* moka_get_symbol(struct moka* self, MOKA value);
MOKA moka_push_ref(struct moka* self, size_t value, int line); MOKA moka_push_ref(struct moka* self, size_t value, int line);
size_t moka_get_ref(struct moka* self, MOKA value); size_t moka_get_ref(struct moka* self, MOKA value);
MOKA moka_push_native(struct moka* self, native_fun_t value, int line); MOKA moka_push_native(struct moka* self,
native_fun_t value,
int arity,
int line);
native_fun_t moka_get_native(struct moka* self, MOKA value); native_fun_t moka_get_native(struct moka* self, MOKA value);
MOKA moka_eval_lazy(struct moka* self, MOKA lazy_value); MOKA moka_eval_lazy(struct moka* self, MOKA lazy_value);

View File

@ -1,9 +1,12 @@
#include "native.h" #include "native.h"
void native_init(struct native* self, native_fun_t fun) void native_init(struct native* self,
native_fun_t fun,
int arity)
{ {
assert(self); assert(self);
self->fun = fun; self->fun = fun;
self->arity = arity;
} }
void native_free(struct native* self) void native_free(struct native* self)

View File

@ -8,13 +8,15 @@ struct moka;
typedef MOKA (*native_fun_t)(struct moka*, struct vec* args); typedef MOKA (*native_fun_t)(struct moka*, struct vec* args);
struct native struct native
{ {
native_fun_t fun; native_fun_t fun;
int arity;
}; };
void native_init(struct native* self, native_fun_t fun); void native_init(struct native* self,
native_fun_t fun,
int arity);
void native_free(struct native* self); void native_free(struct native* self);
#endif #endif