From 9d9d5d5be7b9b269bc2cd023b6966d3d4e6b9a7f Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Sat, 16 Jan 2021 10:18:19 +0900 Subject: [PATCH] Add math module --- Makefile | 3 + src/math.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 src/math.c diff --git a/Makefile b/Makefile index 87e504f..5d582bb 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,9 @@ kuroko: ${KUROKO_LIBS} modules/%.so: src/%.c ${CC} ${CFLAGS} -shared -o $@ $< +modules/math.so: src/math.c + ${CC} ${CFLAGS} -shared -o $@ $< -lm + libkuroko.so: ${OBJS} ${CC} ${CLFAGS} -shared -o $@ ${OBJS} diff --git a/src/math.c b/src/math.c new file mode 100644 index 0000000..4068e50 --- /dev/null +++ b/src/math.c @@ -0,0 +1,183 @@ +/** + * math module; thin wrapper around libc math functions. + */ +#include +#include "../vm.h" +#include "../value.h" +#include "../object.h" + +#define S(c) (krk_copyString(c,sizeof(c)-1)) + +#define ONE_ARGUMENT(name) if (argc != 1) { \ + krk_runtimeError(vm.exceptions.argumentError, "%s() expects one argument", #name); \ + return NONE_VAL(); \ +} + +#define TWO_ARGUMENTS(name) if (argc != 2) { \ + krk_runtimeError(vm.exceptions.argumentError, "%s() expects two arguments", #name); \ + return NONE_VAL(); \ +} + +#define FORCE_FLOAT(arg) \ + if (!IS_FLOATING(arg)) { switch (arg.type) { \ + case VAL_INTEGER: arg = FLOATING_VAL(AS_INTEGER(arg)); break; \ + case VAL_BOOLEAN: arg = FLOATING_VAL(AS_BOOLEAN(arg)); break; \ + default: { \ + KrkClass * type = AS_CLASS(krk_typeOf(1,&arg)); \ + krk_push(arg); \ + if (!krk_bindMethod(type, S("__float__"))) { \ + krk_pop(); \ + } else { \ + arg = krk_callSimple(krk_peek(0), 0, 1); \ + } \ + } break; \ + } } + +#define REAL_NUMBER_NOT(name, garbage) { \ + krk_runtimeError(vm.exceptions.typeError, "%s() argument must be real number, not %s", #name, krk_typeName(garbage)); \ + return NONE_VAL(); \ +} + +#define MATH_DELEGATE(func) \ +static KrkValue _math_ ## func(int argc, KrkValue argv[]) { \ + ONE_ARGUMENT(func) \ + if (IS_FLOATING(argv[0])) { \ + return INTEGER_VAL(func(AS_FLOATING(argv[0]))); \ + } else if (IS_INTEGER(argv[0])) { \ + return argv[0]; /* no op */ \ + } else { \ + KrkClass * type = AS_CLASS(krk_typeOf(1,&argv[0])); \ + krk_push(argv[0]); \ + if (!krk_bindMethod(type, S("__" #func "__"))) REAL_NUMBER_NOT(func,argv[0]) \ + return krk_callSimple(krk_peek(0), 0, 1); \ + } \ +} + +MATH_DELEGATE(ceil) +MATH_DELEGATE(floor) +MATH_DELEGATE(trunc) + +#define MATH_ONE_NAME(func,name) \ +static KrkValue _math_ ## name(int argc, KrkValue argv[]) { \ + ONE_ARGUMENT(name) \ + FORCE_FLOAT(argv[0]) \ + if (IS_FLOATING(argv[0])) { \ + return FLOATING_VAL(func(AS_FLOATING(argv[0]))); \ + } else REAL_NUMBER_NOT(name,argv[0]) \ +} +#define MATH_ONE(func) MATH_ONE_NAME(func,func) + +MATH_ONE(exp) +MATH_ONE(expm1) +MATH_ONE(log2) +MATH_ONE(log10) +MATH_ONE(sqrt) +MATH_ONE(acos) +MATH_ONE(asin) +MATH_ONE(atan) +MATH_ONE(cos) +MATH_ONE(sin) +MATH_ONE(tan) +MATH_ONE(acosh) +MATH_ONE(asinh) +MATH_ONE(atanh) +MATH_ONE(cosh) +MATH_ONE(sinh) +MATH_ONE(tanh) +MATH_ONE(erf) +MATH_ONE(erfc) +MATH_ONE(gamma) +MATH_ONE(lgamma) +MATH_ONE_NAME(log,log1p) + +#define MATH_TWO(func) \ +static KrkValue _math_ ## func(int argc, KrkValue argv[]) { \ + TWO_ARGUMENTS(func) \ + FORCE_FLOAT(argv[0]) \ + FORCE_FLOAT(argv[1]) \ + if (!IS_FLOATING(argv[0])) REAL_NUMBER_NOT(func,argv[0]) \ + if (!IS_FLOATING(argv[1])) REAL_NUMBER_NOT(func,argv[1]) \ + return FLOATING_VAL(func(AS_FLOATING(argv[0]),AS_FLOATING(argv[1]))); \ +} + +MATH_TWO(copysign) +MATH_TWO(fmod) +MATH_TWO(remainder) +MATH_TWO(pow) +MATH_TWO(atan2) + +static KrkValue _math_frexp(int argc, KrkValue argv[]) { + ONE_ARGUMENT(frexp) + FORCE_FLOAT(argv[0]) + if (!IS_FLOATING(argv[0])) { + REAL_NUMBER_NOT(frexp,argv[0]) + } + int exp = 0; + double result = frexp(AS_FLOATING(argv[0]), &exp); + KrkTuple * outValue = krk_newTuple(2); + outValue->values.values[0] = FLOATING_VAL(result); + outValue->values.values[1] = INTEGER_VAL(exp); + outValue->values.count = 2; + return OBJECT_VAL(outValue); +} + +#define MATH_IS(func) \ +static KrkValue _math_ ## func(int argc, KrkValue argv[]) { \ + ONE_ARGUMENT(func) \ + if (!IS_FLOATING(argv[0])) REAL_NUMBER_NOT(func,argv[0]) \ + return BOOLEAN_VAL(func(AS_FLOATING(argv[0]))); \ +} + +MATH_IS(isfinite) +MATH_IS(isinf) +MATH_IS(isnan) + +#define bind(name) krk_defineNative(&module->fields, #name, _math_ ## name) + +KrkValue krk_module_onload_math(void) { + KrkInstance * module = krk_newInstance(vm.moduleClass); + krk_push(OBJECT_VAL(module)); + + bind(ceil); + bind(floor); + bind(trunc); + bind(exp); + bind(expm1); + bind(log2); + bind(log10); + bind(sqrt); + bind(acos); + bind(asin); + bind(atan); + bind(cos); + bind(sin); + bind(tan); + bind(acosh); + bind(asinh); + bind(atanh); + bind(cosh); + bind(sinh); + bind(tanh); + bind(erf); + bind(erfc); + bind(gamma); + bind(lgamma); + bind(copysign); + bind(fmod); + bind(remainder); + bind(log1p); + bind(pow); + bind(atan2); + bind(frexp); + bind(isfinite); + bind(isinf); + bind(isnan); + + krk_attachNamedValue(&module->fields, "pi", FLOATING_VAL(M_PI)); + krk_attachNamedValue(&module->fields, "e", FLOATING_VAL(M_E)); + krk_attachNamedValue(&module->fields, "inf", FLOATING_VAL(INFINITY)); + krk_attachNamedValue(&module->fields, "nan", FLOATING_VAL(NAN)); + + krk_pop(); + return OBJECT_VAL(module); +}