#include #include #include #include #include #include "janet.h" using std::string; // set all our exported Janet functions as null pointers static JanetFunction *janet_ludus = NULL; // these let us look up functions Janet env_lookup(JanetTable *env, const char *name) { Janet out; janet_resolve(env, janet_csymbol(name), &out); return out; } JanetFunction *env_lookup_function(JanetTable *env, const char *name) { Janet value = env_lookup(env, name); if (!janet_checktype(value, JANET_FUNCTION)) { janet_panicf("expected %s to be a function, got %q\n", name, value); } return janet_unwrap_function(value); } // this lets us call a function bool call_fn(JanetFunction *fn, int argc, const Janet *argv, Janet *out) { JanetFiber *fiber = NULL; if (janet_pcall(fn, argc, argv, out, &fiber) == JANET_SIGNAL_OK) { return true; } else { janet_stacktrace(fiber, *out); return false; } } // this is darkish magic, reads an embedded file // do not fuck with this, fellas unsigned char *read_file(const char *filename, size_t *length) { size_t capacity = 2 << 17; unsigned char *src = (unsigned char *)malloc(capacity * sizeof(unsigned char)); assert(src); size_t total_bytes_read = 0; FILE *file = fopen(filename, "r"); assert(file); size_t bytes_read; do { size_t remaining_capacity = capacity - total_bytes_read; if (remaining_capacity == 0) { capacity <<= 1; src = (unsigned char*)realloc(src, capacity * sizeof(unsigned char)); assert(src); remaining_capacity = capacity - total_bytes_read; } bytes_read = fread(&src[total_bytes_read], sizeof(unsigned char), remaining_capacity, file); total_bytes_read += bytes_read; } while (bytes_read > 0); fclose(file); *length = total_bytes_read; return src; } // finally, getting a string back // this is our result type struct StringResult { string value; }; // this is our result constructor // Janet's getcstring resturns const char* StringResult string_result(const char* cstr) { // ...which we have to cast to a std::string return (StringResult) {.value = (string) cstr }; } // and this is a function that takes and returns a string // it returns a StringResult, tho StringResult ludus(string source) { Janet result; const Janet args[1] = {janet_cstringv(source.c_str())}; call_fn(janet_ludus, 1, args, &result); // get the cstring in the result // the 0 passed here is the index in the result of the string const char* cstr = janet_getcstring(&result, 0); // return a constructed StringResult return string_result(cstr); } // This function sets up our Janet interpreter, and fixes the null pointers EMSCRIPTEN_KEEPALIVE int main() { janet_init(); // start the interpreter JanetTable *core_env = janet_core_env(NULL); // get a core env JanetTable *lookup = janet_env_lookup(core_env); // and get an env table // load the janet image into memory // note that the image is hardcoded here size_t image_length; unsigned char *image = read_file("ludus.jimage", &image_length); // load the image into the Janet environment Janet env = janet_unmarshal(image, image_length, 0, lookup, NULL); if(!janet_checktype(env, JANET_TABLE)) { janet_panicf("invalid image %q", env); } // fix the null pointers, as above // note that the bound symbols are just the normal fn names // no namespacing janet_ludus = env_lookup_function(janet_unwrap_table(env), "ludus"); janet_gcroot(janet_wrap_function(janet_ludus)); } // these bindings are exported into javascript EMSCRIPTEN_BINDINGS(module) { using namespace emscripten; // these are the functions that will be available function("ludus", &ludus, allow_raw_pointers()); // we also want a wrapper for our StringResult // we won't access it directly, but emcc makes it nice value_object("StringResult") .field("value", &StringResult::value); }