module_math.c
1 
4 #include <math.h>
5 #include <kuroko/vm.h>
6 #include <kuroko/value.h>
7 #include <kuroko/object.h>
8 #include <kuroko/util.h>
9 
10 #define ONE_ARGUMENT(name) if (argc != 1) { \
11  krk_runtimeError(vm.exceptions->argumentError, "%s() expects one argument", #name); \
12  return NONE_VAL(); \
13 }
14 
15 #define TWO_ARGUMENTS(name) if (argc != 2) { \
16  krk_runtimeError(vm.exceptions->argumentError, "%s() expects two arguments", #name); \
17  return NONE_VAL(); \
18 }
19 
20 #define FORCE_FLOAT(src,arg) \
21  KrkValue arg = src; \
22  if (!IS_FLOATING(arg)) { switch (KRK_VAL_TYPE(arg)) { \
23  case KRK_VAL_INTEGER: arg = FLOATING_VAL(AS_INTEGER(arg)); break; \
24  case KRK_VAL_BOOLEAN: arg = FLOATING_VAL(AS_BOOLEAN(arg)); break; \
25  default: { \
26  KrkClass * type = krk_getType(arg); \
27  krk_push(arg); \
28  if (!krk_bindMethod(type, S("__float__"))) { \
29  krk_pop(); \
30  } else { \
31  arg = krk_callStack(0); \
32  } \
33  } break; \
34  } }
35 
36 #define REAL_NUMBER_NOT(name, garbage) { \
37  krk_runtimeError(vm.exceptions->typeError, "%s() argument must be real number, not '%T'", #name, garbage); \
38  return NONE_VAL(); \
39 }
40 
41 extern KrkValue krk_int_from_float(double val);
42 
43 #define MATH_DELEGATE(func) \
44 static KrkValue _math_ ## func(int argc, const KrkValue argv[], int hasKw) { \
45  ONE_ARGUMENT(func) \
46  if (IS_FLOATING(argv[0])) { \
47  return krk_int_from_float(func(AS_FLOATING(argv[0]))); \
48  } else if (IS_INTEGER(argv[0])) { \
49  return argv[0]; /* no op */ \
50  } else { \
51  KrkClass * type = krk_getType(argv[0]); \
52  krk_push(argv[0]); \
53  if (!krk_bindMethod(type, S("__" #func "__"))) REAL_NUMBER_NOT(func,argv[0]) \
54  return krk_callStack(0); \
55  } \
56 }
57 
58 MATH_DELEGATE(ceil)
59 MATH_DELEGATE(floor)
60 MATH_DELEGATE(trunc)
61 
62 #define MATH_ONE_NAME(func,name) \
63 static KrkValue _math_ ## name(int argc, const KrkValue argv[], int hasKw) { \
64  ONE_ARGUMENT(name) \
65  FORCE_FLOAT(argv[0],arg_0) \
66  if (IS_FLOATING(arg_0)) { \
67  return FLOATING_VAL(func(AS_FLOATING(arg_0))); \
68  } else REAL_NUMBER_NOT(name,arg_0) \
69 }
70 #define MATH_ONE(func) MATH_ONE_NAME(func,func)
71 
72 MATH_ONE(exp)
73 MATH_ONE(expm1)
74 MATH_ONE(log2)
75 MATH_ONE(log10)
76 MATH_ONE(sqrt)
77 MATH_ONE(acos)
78 MATH_ONE(asin)
79 MATH_ONE(atan)
80 MATH_ONE(cos)
81 MATH_ONE(sin)
82 MATH_ONE(tan)
83 MATH_ONE(acosh)
84 MATH_ONE(asinh)
85 MATH_ONE(atanh)
86 MATH_ONE(cosh)
87 MATH_ONE(sinh)
88 MATH_ONE(tanh)
89 MATH_ONE(erf)
90 MATH_ONE(erfc)
91 MATH_ONE(log1p)
92 MATH_ONE(tgamma)
93 MATH_ONE(lgamma)
94 
95 #define MATH_TWO(func) \
96 static KrkValue _math_ ## func(int argc, const KrkValue argv[], int hasKw) { \
97  TWO_ARGUMENTS(func) \
98  FORCE_FLOAT(argv[0],arg_0) \
99  FORCE_FLOAT(argv[1],arg_1) \
100  if (!IS_FLOATING(arg_0)) REAL_NUMBER_NOT(func,arg_0) \
101  if (!IS_FLOATING(arg_1)) REAL_NUMBER_NOT(func,arg_1) \
102  return FLOATING_VAL(func(AS_FLOATING(arg_0),AS_FLOATING(arg_1))); \
103 }
104 
105 MATH_TWO(copysign)
106 MATH_TWO(fmod)
107 MATH_TWO(remainder)
108 MATH_TWO(pow)
109 MATH_TWO(atan2)
110 
111 static KrkValue _float_pow(int argc, const KrkValue argv[], int hasKw) {
112  TWO_ARGUMENTS(__pow__);
113  if (unlikely(!IS_FLOATING(argv[0]))) return krk_runtimeError(vm.exceptions->typeError, "expected float");
114  if (likely(IS_FLOATING(argv[1]))) {
115  return FLOATING_VAL(pow(AS_FLOATING(argv[0]),AS_FLOATING(argv[1])));
116  } else if (likely(IS_INTEGER(argv[1]))) {
117  return FLOATING_VAL(pow(AS_FLOATING(argv[0]),(double)AS_INTEGER(argv[1])));
118  }
119  return NOTIMPL_VAL();
120 }
121 
122 
123 static KrkValue _math_frexp(int argc, const KrkValue argv[], int hasKw) {
124  ONE_ARGUMENT(frexp)
125  FORCE_FLOAT(argv[0],arg_0)
126  if (!IS_FLOATING(arg_0)) {
127  REAL_NUMBER_NOT(frexp,arg_0)
128  }
129  int exp = 0;
130  double result = frexp(AS_FLOATING(arg_0), &exp);
131  KrkTuple * outValue = krk_newTuple(2);
132  outValue->values.values[0] = FLOATING_VAL(result);
133  outValue->values.values[1] = INTEGER_VAL(exp);
134  outValue->values.count = 2;
135  return OBJECT_VAL(outValue);
136 }
137 
138 #define MATH_IS(func) \
139 static KrkValue _math_ ## func(int argc, const KrkValue argv[], int hasKw) { \
140  ONE_ARGUMENT(func) \
141  if (!IS_FLOATING(argv[0])) REAL_NUMBER_NOT(func,argv[0]) \
142  return BOOLEAN_VAL(func(AS_FLOATING(argv[0]))); \
143 }
144 
145 MATH_IS(isfinite)
146 MATH_IS(isinf)
147 MATH_IS(isnan)
148 
149 #define bind(name) krk_defineNative(&module->fields, #name, _math_ ## name)
150 
151 KRK_Module(math) {
152  KRK_DOC(module, "@brief Provides access to floating-point mathematical functions from the system `libm`.");
153  KRK_DOC(bind(ceil),
154  "@brief Returns the smallest integer value not less than the input.\n"
155  "@arguments x");
156  KRK_DOC(bind(floor),
157  "@brief Returns the largest integer value not greater than the input.\n"
158  "@arguments x");
159  KRK_DOC(bind(trunc),
160  "@brief Rounds the input towards zero to an integer.\n"
161  "@arguments x");
162  KRK_DOC(bind(exp),
163  "@brief Returns the base-e exponentiation of the input.\n"
164  "@arguments x");
165  KRK_DOC(bind(expm1),
166  "@brief Equivalent to `exp(x) - 1`.\n"
167  "@arguments x");
168  KRK_DOC(bind(log2),
169  "@brief Calculates the base-2 logarithm of the input.\n"
170  "@arguments x");
171  KRK_DOC(bind(log10),
172  "@brief Calculates the base-10 logarithm of the input.\n"
173  "@arguments x");
174  KRK_DOC(bind(sqrt),
175  "@brief Calculates the square root of the input.\n"
176  "@arguments x");
177  KRK_DOC(bind(acos),
178  "@brief Calculates the arc-cosine of the radian input.\n"
179  "@arguments x");
180  KRK_DOC(bind(asin),
181  "@brief Calculates the arc-sine of the radian input.\n"
182  "@arguments x");
183  KRK_DOC(bind(atan),
184  "@brief Calculates the arc-tangent of the radian input.\n"
185  "@arguments x");
186  KRK_DOC(bind(cos),
187  "@brief Calculates the cosine of the radian input.\n"
188  "@arguments x");
189  KRK_DOC(bind(sin),
190  "@brief Calculates the sine of the radian input.\n"
191  "@arguments x");
192  KRK_DOC(bind(tan),
193  "@brief Calculates the tangent of the radian input.\n"
194  "@arguments x");
195  KRK_DOC(bind(acosh),
196  "@brief Calculates the inverse hyperbolic cosine of the input.\n"
197  "@arguments x");
198  KRK_DOC(bind(asinh),
199  "@brief Calculates the inverse hyperbolic sine of the input.\n"
200  "@arguments x");
201  KRK_DOC(bind(atanh),
202  "@brief Calculates the inverse hyperbolic tangent of the input.\n"
203  "@arguments x");
204  KRK_DOC(bind(cosh),
205  "@brief Calculates the hyperbolic cosine of the input.\n"
206  "@arguments x");
207  KRK_DOC(bind(sinh),
208  "@brief Calculates the hyperbolic sine of the input.\n"
209  "@arguments x");
210  KRK_DOC(bind(tanh),
211  "@brief Calculates the hyperbolic tangent of the input.\n"
212  "@arguments x");
213  KRK_DOC(bind(erf),
214  "@brief Calculates the error function of the input.\n"
215  "@arguments x");
216  KRK_DOC(bind(erfc),
217  "@brief Calculates the complementary error function of the input.\n"
218  "@arguments x");
219  KRK_DOC(bind(tgamma),
220  "@brief Calculates the gamma of the input.\n"
221  "@arguments x");
222  KRK_DOC(bind(lgamma),
223  "@brief Calculates the log gamma of the input.\n"
224  "@arguments x");
225  KRK_DOC(bind(copysign),
226  "@brief Copies the sign from @p x to @p y\n"
227  "@arguments x,y");
228  KRK_DOC(bind(fmod),
229  "@brief Returns the floating point remainder of @p x over @p y\n"
230  "@arguments x,y");
231  KRK_DOC(bind(remainder),
232  "@brief Somehow different from `fmod`.");
233  KRK_DOC(bind(log1p),
234  "@brief Equivalent to `log(x + 1)`\n"
235  "@arguments x");
236  KRK_DOC(bind(expm1),
237  "@brief Equivalent to `exp(x) - 1`\n"
238  "@arguments x");
239  KRK_DOC(bind(pow),
240  "@brief Calculates `x^p`\n"
241  "@arguments x,p");
242  KRK_DOC(bind(atan2),
243  "@brief Calculates the arctangent of `x` and `y`\n"
244  "@arguments x,y");
245  KRK_DOC(bind(frexp),
246  "@brief Converts a floating point input to a fractional and integer component pair, returned as a tuple.\n"
247  "@arguments x\n"
248  "@returns @ref tuple of two @ref int");
249  KRK_DOC(bind(isfinite),
250  "@brief Determines if the input is finite.\n"
251  "@arguments x\n");
252  KRK_DOC(bind(isinf),
253  "@brief Determines if the input is infinite.\n"
254  "@arguments x\n");
255  KRK_DOC(bind(isnan),
256  "@brief Determines if the input is the floating point `NaN`.\n"
257  "@arguments x\n");
258 
264  krk_defineNative(&vm.baseClasses->floatClass->methods, "__pow__", _float_pow);
265  krk_finalizeClass(vm.baseClasses->floatClass);
266 
267  krk_attachNamedValue(&module->fields, "pi", FLOATING_VAL(M_PI));
268  krk_attachNamedValue(&module->fields, "e", FLOATING_VAL(M_E));
269  krk_attachNamedValue(&module->fields, "inf", FLOATING_VAL(INFINITY));
270  krk_attachNamedValue(&module->fields, "nan", FLOATING_VAL(NAN));
271 }
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:460
KRK_Module(socket)
Struct definitions for core object types.
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
Definition: vm.c:189
KrkNative * krk_defineNative(KrkTable *table, const char *name, NativeFn function)
Attach a native C function to an attribute table.
Definition: vm.c:155
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
Definition: vm.c:794
Immutable sequence of arbitrary values.
Definition: object.h:323
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
Definition: object.h:325
KrkTuple * krk_newTuple(size_t length)
Create a new tuple.
Definition: object.c:357
KrkValue * values
Definition: value.h:78
size_t count
Definition: value.h:77
Stack reference or primative value.
Utilities for creating native bindings.
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:304
Definitions for primitive stack references.
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
Definition: vm.h:257