131 lines
3.9 KiB
C++
131 lines
3.9 KiB
C++
#include <cstdint>
|
|
#include <emscripten.h>
|
|
#include <emscripten/bind.h>
|
|
#include <string>
|
|
#include <stdio.h>
|
|
#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>("StringResult")
|
|
.field("value", &StringResult::value);
|
|
}
|