I’ve been working on Brat quite a bit, and I remembered that I wanted to add in BigNum support. There is a GNU library for this called gmplib which seemed fairly straightforward. In fact, it was considerably more straightforward than connecting it up with Neko.
By the way, I realized I have never done anything in C. My undergraduate courses started off with C++, but I have never had to use plain old C.
Anyhow, this project required wired up three things: gmplib to Neko, then Neko to Brat. gmplib to Neko required creating, manipulating, and wrapping up C code and values using Neko’s FFI. Then I needed to replace all the math code I had written for Brat (in Neko). The last step (unfinished as of yet) will be to seamlessly switch between using Neko’s number values and the structures used by gmplib. There is no point in wasting time with BigNums when you are doing 1 + 1.
Neko FFI
First of all, everything passed in or out of Neko will be avalue. This is a Neko datatype, which basically contains a kind and the data itself. There are several kinds that are predefined (basically, a kind is a type), but you will likely need to make your own to pass C values in and out of Neko. This is what the top of my number.c file looks like:
#include <stdio.h>
#include <neko.h>
#include <gmp.h>
DEFINE_KIND(k_mint);
DEFINE_KIND(k_mfloat);
I include the proper header files and then define two kinds, one for integers and another for floats.
I then made a couple functions (one for integers, one for floats) to allocate and wrap up the proper structures for me. I think this is probably the way it would typically look when using Neko’s FFI:
value new_float() {
mpf_t * new_float;
new_float = (mpf_t*)alloc(sizeof(mpf_t));
mpf_init(*new_float);
value store = alloc_abstract(k_mfloat, new_float);
val_gc(store, free_float);
return store;
}
The gmplib structures need to be cleared when you are done with them, so I have a function which is used as a ‘finalizer’, which is assigned as shown above. When the Neko value is garbage collected, it first calls this method:
void free_float(value floatval) {
mpf_clear(val_data(floatval));
}
This function demonstrates another item: getting the data out of a value. This is performed using val_data, which returns whatever data you have wrapped up in the Neko value (using alloc_abstract).
value float_add(value float1, value float2) {
value result = new_float();
mpf_add(val_data(result), val_data(float1), val_data(float2));
return result;
}
To make these functions available to Neko, all you have to do is declare them to be ‘primitives’ and provide the number of parameters:
DEFINE_PRIM(num_add, 2);
Note that all values returned to Neko need to be converted into Neko values, even things like ints and booleans. This is fairly simple, though, as the API provides functions for these, as well as for checking the types of values passed in from Neko. For example, here is a simple boolean check:
value is_int(value num) {
return alloc_bool(val_is_kind(num, k_mint));
}
Once I caught the hang of it, wrapping up the library functions wasn’t too hard. But good thing I have a fairly large test suite to make sure I’m doing everything right! This is the first project where I have used tests like this, but I believe I understand why people are stressing tests so much. It’s amazing how much they help, both in finding bugs and in providing some peace of mind.

