From 1c5ad117d8ecad785ba2261ab2f1c6572d138e66 Mon Sep 17 00:00:00 2001 From: bog Date: Sat, 30 Mar 2024 17:24:52 +0100 Subject: [PATCH] :sparkles: comparison operators, :white_check_mark: acceptance tests. --- features/cmp.mk | 24 +++++++++++ features/run.sh | 30 ++++++++++++++ lib/builtins.c | 107 ++++++++++++++++++++++++++++++++++++++++++++---- lib/builtins.h | 9 ++++ lib/moka.c | 88 +++++++++++++++++++++++++++++++++++---- lib/moka.h | 11 ++++- lib/native.c | 5 ++- lib/native.h | 6 ++- 8 files changed, 261 insertions(+), 19 deletions(-) create mode 100644 features/cmp.mk create mode 100755 features/run.sh diff --git a/features/cmp.mk b/features/cmp.mk new file mode 100644 index 0000000..8e9742e --- /dev/null +++ b/features/cmp.mk @@ -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])) diff --git a/features/run.sh b/features/run.sh new file mode 100755 index 0000000..c5f7261 --- /dev/null +++ b/features/run.sh @@ -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 diff --git a/lib/builtins.c b/lib/builtins.c index 0b93af1..f999bbd 100644 --- a/lib/builtins.c +++ b/lib/builtins.c @@ -7,10 +7,17 @@ void register_builtins(struct moka* moka) { assert(moka); - moka_decl_native(moka, "println", mk_println); - moka_decl_native(moka, "define", mk_define); - moka_decl_native(moka, "array", mk_array); - moka_decl_native(moka, "assert-eqv!", mk_assert_eqv_mut); + moka_decl_native(moka, "println", mk_println, -1); + moka_decl_native(moka, "define", mk_define, 2); + moka_decl_native(moka, "array", mk_array, -1); + 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) @@ -88,8 +95,94 @@ MOKA mk_assert_eqv_mut(struct moka* moka, struct vec* args) "assertion failed\nlhs = %s\nrhs = %s", lhs_buf, rhs_buf); - return moka_push_bool(moka, false, line); + return moka_push_bool(moka, false, line); } - - 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) + ); } diff --git a/lib/builtins.h b/lib/builtins.h index c990f38..0fdfade 100644 --- a/lib/builtins.h +++ b/lib/builtins.h @@ -13,4 +13,13 @@ MOKA mk_define(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); +// 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 diff --git a/lib/moka.c b/lib/moka.c index c4448db..18de3b6 100644 --- a/lib/moka.c +++ b/lib/moka.c @@ -100,7 +100,8 @@ void moka_import_module(struct moka* self, moka_decl_native( self, new_name, - val->data.native->fun + val->data.native->fun, + val->data.native->arity ); } break; @@ -181,10 +182,11 @@ void moka_decl_var(struct moka* self, void moka_decl_native(struct moka* self, char* name, - native_fun_t fun) + native_fun_t fun, + int arity) { struct native* native = malloc(sizeof(struct native)); - native_init(native, fun); + native_init(native, fun, arity); struct value* value = malloc(sizeof(struct value)); value_init_native(value, native, 0); @@ -238,7 +240,7 @@ int moka_line(struct moka* self, MOKA value) return val->line; } -size_t moka_str(struct moka* self, MOKA value, +size_t moka_str(struct moka* self, MOKA value, char* buffer, size_t size) { struct frame* frame = moka_frame(self); @@ -252,6 +254,60 @@ size_t moka_str(struct moka* self, MOKA value, 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) { struct frame* frame = moka_frame(self); @@ -262,7 +318,7 @@ bool moka_is_eqv(struct moka* self, MOKA mk_lhs, MOKA mk_rhs) { return false; } - + if (moka_is(self, mk_lhs, TY_REF)) { lhs = self->global_values.data[lhs->data.ref]; @@ -390,10 +446,12 @@ MOKA moka_call(struct moka* self, int arg_count) MOKA fun = moka_pop(self); struct vec args; vec_init(&args); + int line = 0; for (ssize_t i=0; itype == TY_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); vec_free(&args); @@ -544,13 +615,16 @@ size_t moka_get_ref(struct moka* self, MOKA value) 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(value); struct native* native = malloc(sizeof(struct native)); - native_init(native, value); + native_init(native, value, arity); struct value* val = malloc(sizeof(struct value)); value_init_native(val, native, line); diff --git a/lib/moka.h b/lib/moka.h index ae340f0..99b6af1 100644 --- a/lib/moka.h +++ b/lib/moka.h @@ -51,7 +51,8 @@ void moka_decl_var(struct moka* self, void moka_decl_native(struct moka* self, char* name, - native_fun_t fun); + native_fun_t fun, + int arity); struct frame* moka_frame(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); size_t moka_str(struct moka* self, MOKA value, 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(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); 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); MOKA moka_eval_lazy(struct moka* self, MOKA lazy_value); diff --git a/lib/native.c b/lib/native.c index 882ce07..80a480e 100644 --- a/lib/native.c +++ b/lib/native.c @@ -1,9 +1,12 @@ #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); self->fun = fun; + self->arity = arity; } void native_free(struct native* self) diff --git a/lib/native.h b/lib/native.h index 7f17ad4..b548e02 100644 --- a/lib/native.h +++ b/lib/native.h @@ -8,13 +8,15 @@ struct moka; typedef MOKA (*native_fun_t)(struct moka*, struct vec* args); - struct native { 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); #endif