vtereshkov / umka-lang

Umka: a statically typed embeddable scripting language
BSD 2-Clause "Simplified" License
1.03k stars 54 forks source link

The need for a bindings generator tool #342

Open luauser32167 opened 7 months ago

luauser32167 commented 7 months ago

Suppose we have the following C library:

mylib.h

#ifndef MYLIB_H
#define MYLIB_H
#include <stdint.h>
int64_t mylib_add(int64_t a, int64_t b);
int64_t mylib_mul(int64_t a, int64_t b);
#endif // MYLIB_H

mylib.c

#include <stdint.h>
#include "mylib.h"
int64_t mylib_add(int64_t a, int64_t b) { return a + b; }
int64_t mylib_mul(int64_t a, int64_t b) { return a * b; }

and we wanted to use it from Umka: script.um

import "mymod.um"
fn main() {
  printf("10 + 20 = %d\n", mymod.add(10, 20));
  printf("30 * 40 = %d\n", mymod.mul(30, 40));
}

We would need to write the following "glue" code:

// fn mymod.add(a, b: int): int;
static void um_mymod_add(UmkaStackSlot* args, UmkaStackSlot* ret) {
  int64_t a = args[1].intVal;
  int64_t b = args[0].intVal;
  ret->intVal = mylib_add(a, b);
}

// fn mymod.mul(a, b: int): int;
static void um_mymod_mul(UmkaStackSlot* args, UmkaStackSlot* ret) {
  int64_t a = args[1].intVal;
  int64_t b = args[0].intVal;
  ret->intVal = mylib_mul(a, b);
}

char const* mymodName = "mymod.um";
static char const* mymodText =
"fn um_mymod_add(a, b: int) : int;\n"
"fn add*(a, b: int) : int { return um_mymod_add(a, b); }\n"
"\n"
"fn um_mymod_mul(a, b: int) : int;\n"
"fn mul*(a, b: int) : int { return um_mymod_mul(a, b); }\n"
"\n"
;

static void bindingsInit(void* U) {
  umkaAddFunc(U, "um_mymod_add", &um_mymod_add);
  umkaAddFunc(U, "um_mymod_mul", &um_mymod_mul);
}

Having to manually write this "glue code" is annoying because for every function that we want to make available to script.um, we need to make changes to 3 places. The first and most tedious is the wrapper function that reads its arguments from the UmkaStackSlot* args, where the last argument is at index 0, next argument is at index 1, etc and writes its results (if any) to UmkaStackSlot* ret. After writing the wrapper function we need to write a declaration for it in Umka fn um_mymod_add(a, b: int) : int; and also the function that users are going to call that just calls it fn add*(a, b: int) : int { return um_mymod_add(a, b); }. And finally we have to register the function umkaAddFunc(U, "um_mymod_add", &um_mymod_add);.

My proposal is that there should be a tool that generates the "glue code" from header files (mylib.h). It doesn't have to be super robust to be useful, it could just handle a subset of C declarations (struct, enum and function declarations) that can be mapped to Umka.

vtereshkov commented 7 months ago

I think @robloach may already have something like this. He was working on Umka bindings for raylib:

https://github.com/robloach/raylib-umka

RobLoach commented 7 months ago

Thanks. Was a fun project to put together. Umka's binding API made it really easy to integrate with raylib. While I could've likely used Umka to parse raylib.h and output the Umka/C bindings, I am lazy and used JavaScript to build the generator.

I'd be more than happy with moving the generator to be a bit more abstract, or ported to Umka itself. Feel free to make some issues in the queue if you have any ideas.