summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2020-01-19 14:44:11 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2020-01-19 14:44:11 -0500
commit304a43226ce9f44dc98d87d2b9ca0316258d6035 (patch)
treee2f083acc40e00019f822725e03372d89e98c741
parent13ff2041474f75647b34c5bec58e3289e56be8df (diff)
#foreign is kinda working now!
-rwxr-xr-xbuild.sh2
-rw-r--r--eval.c2
-rw-r--r--foreign.c260
-rw-r--r--test.toc20
-rw-r--r--toc.c4
-rw-r--r--types.c6
6 files changed, 285 insertions, 9 deletions
diff --git a/build.sh b/build.sh
index 6ca55e6..4b69315 100755
--- a/build.sh
+++ b/build.sh
@@ -21,7 +21,7 @@ else
WARNINGS=''
fi
-DEBUG_FLAGS="-O0 -g3 $WARNINGS -std=c11 -DTOC_DEBUG"
+DEBUG_FLAGS="-O0 -g3 $WARNINGS -std=c11 -DTOC_DEBUG -DCOMPILE_TIME_FOREIGN_FN_SUPPORT=1 -lffcall -ldl"
RELEASE_FLAGS="-O3 -s -DNDEBUG $WARNINGS -std=c11"
if [ "$1" = "release" ]; then
diff --git a/eval.c b/eval.c
index 3fc414e..b3a9c5c 100644
--- a/eval.c
+++ b/eval.c
@@ -1435,7 +1435,7 @@ static bool eval_expr(Evaluator *ev, Expression *e, Value *v) {
if (!eval_expr(ev, &e->call.arg_exprs[i], &args[i]))
return false;
}
- bool success = foreign_call(fn, &e->call.fn->type, args, e->where);
+ bool success = foreign_call(fn, &e->call.fn->type, args, e->where, v);
free(args);
if (!success)
return false;
diff --git a/foreign.c b/foreign.c
index 7d441be..cad605c 100644
--- a/foreign.c
+++ b/foreign.c
@@ -1,7 +1,263 @@
+/* WARNING: In this file, you will find crazy macros and dubious usage of avcall. Beware! */
+
#if COMPILE_TIME_FOREIGN_FN_SUPPORT
-static bool foreign_call(FnExpr *fn, Type *fn_type, Value *args, Location call_where) {
+#if CHAR_BIT != 8
+#error "Compile-time foreign functions can only be used on systems where CHAR_BIT is 8."
+#endif
+#include <avcall.h>
+#include <dlfcn.h>
+
+
+#if SCHAR_MAX != 127
+#error "Foreign function support requires an 8-bit signed type."
+#else
+#define av_type_with_limit_127 schar
+#endif
+
+#if UCHAR_MAX != 255
+#error "Foreign function support requires an 8-bit unsigned type."
+#else
+#define av_type_with_limit_255 uchar
+#endif
+
+#if SHRT_MAX != 32767
+#error "Foreign function support requires a 16-bit signed type."
+#else
+#define av_type_with_limit_32767 short
+#endif
+
+#if USHRT_MAX != 65535
+#error "Foreign function support requires an 16-bit unsigned type."
+#else
+#define av_type_with_limit_65535 ushort
+#endif
+
+#if INT_MAX == 2147483647
+#define av_type_with_limit_2147483647 int
+#elif LONG_MAX == 2147483647
+#define av_type_with_limit_2147483647 long
+#else
+#error "Foreign function support requires an 32-bit signed type."
+#endif
+
+#if UINT_MAX == 4294967295
+#define av_type_with_limit_4294967295 uint
+#elif ULONG_MAX == 4294967295
+#define av_type_with_limit_4294967295 ulong
+#else
+#error "Foreign function support requires an 32-bit unsigned type."
+#endif
+
+#if LONG_MAX == 9223372036854775807
+#define av_type_with_limit_9223372036854775807 long
+#elif LLONG_MAX == 9223372036854775807
+#define av_type_with_limit_9223372036854775807 longlong
+#else
+#error "Foreign function support requires a 64-bit signed type."
+#endif
+
+#if ULONG_MAX == 18446744073709551615ULL
+#define av_type_with_limit_18446744073709551615 ulong
+#elif ULLONG_MAX == 18446744073709551615ULL
+#define av_type_with_limit_18446744073709551615 ulonglong
+#else
+#error "Foreign function support requires a 64-bit unsigned type."
+#endif
+
+
+
+#define av_type_with_limit(x) join(av_type_with_limit_, x)
+
+#define toc_av_start(limit) join(av_start_, av_type_with_limit(limit))
+#define toc_av_add(limit) join(av_, av_type_with_limit(limit))
+
+#define toc_av_start_f32 av_start_float
+#define toc_av_start_f64 av_start_double
+#define toc_av_f32 av_float
+#define toc_av_f64 av_double
+
+static bool arg_list_start(av_alist *arg_list, void (*fn)(), Value *return_val, Type *return_type, Location where) {
+ switch (return_type->kind) {
+ case TYPE_UNKNOWN:
+ err_print(where, "Cannot call foreign function with unknown return type.");
+ return false;
+ case TYPE_TUPLE:
+ err_print(where, "Cannot call foreign function with tuple return type.");
+ return false;
+ case TYPE_ARR:
+ err_print(where, "Foreign functions cannot return arrays.");
+ return false;
+ case TYPE_FN:
+ warn_print(where, "Foreign function returns function pointer. If it returns a C-style function pointer, it won't be called properly by toc.");
+ av_start_ptr(*arg_list, fn, FnExpr *, &return_val->fn);
+ break;
+ case TYPE_VOID:
+ av_start_void(*arg_list, fn);
+ break;
+ case TYPE_PTR:
+ av_start_ptr(*arg_list, fn, void *, &return_val->ptr);
+ break;
+ case TYPE_BUILTIN:
+ switch (return_type->builtin) {
+ case BUILTIN_I8:
+ toc_av_start(127)(*arg_list, fn, &return_val->i8);
+ break;
+ case BUILTIN_U8:
+ toc_av_start(255)(*arg_list, fn, &return_val->u8);
+ break;
+ case BUILTIN_I16:
+ toc_av_start(32767)(*arg_list, fn, &return_val->i16);
+ break;
+ case BUILTIN_U16:
+ toc_av_start(65535)(*arg_list, fn, &return_val->u16);
+ break;
+ case BUILTIN_I32:
+ toc_av_start(2147483647)(*arg_list, fn, &return_val->i32);
+ break;
+ case BUILTIN_U32:
+ toc_av_start(4294967295)(*arg_list, fn, &return_val->u32);
+ break;
+ case BUILTIN_I64:
+ toc_av_start(9223372036854775807)(*arg_list, fn, &return_val->i64);
+ break;
+ case BUILTIN_U64:
+ toc_av_start(18446744073709551615)(*arg_list, fn, &return_val->u64);
+ break;
+ case BUILTIN_BOOL:
+ /* bool is probably just unsigned char.... hopefully... */
+ av_start_uchar(*arg_list, fn, &return_val->u8);
+ break;
+ case BUILTIN_CHAR:
+ av_start_char(*arg_list, fn, &return_val->charv);
+ break;
+ case BUILTIN_F32:
+ toc_av_start_f32(*arg_list, fn, &return_val->f32);
+ break;
+ case BUILTIN_F64:
+ toc_av_start_f64(*arg_list, fn, &return_val->f64);
+ break;
+ case BUILTIN_TYPE:
+ av_start_ptr(*arg_list, fn, Type *, &return_val->type);
+ break;
+ case BUILTIN_PKG:
+ av_start_ptr(*arg_list, fn, Package *, &return_val->pkg);
+ break;
+ }
+ break;
+ case TYPE_EXPR:
+ assert(0);
+ return false;
+ }
+ return true;
+}
+
+static bool arg_list_add(av_alist *arg_list, Value *val, Type *type, Location where) {
+ switch (type->kind) {
+ case TYPE_VOID:
+ case TYPE_TUPLE:
+ case TYPE_UNKNOWN:
+ case TYPE_ARR: { /* TODO: maybe just pass pointer for arr? */
+ char *s = type_to_str(type);
+ err_print(where, "Cannot pass type %s to foreign function.", s);
+ free(s);
+ return false;
+ }
+ case TYPE_PTR:
+ av_ptr(*arg_list, void *, val->ptr);
+ break;
+ case TYPE_BUILTIN:
+ switch (type->builtin) {
+ case BUILTIN_I8:
+ toc_av_add(127)(*arg_list, val->i8);
+ break;
+ case BUILTIN_U8:
+ toc_av_add(255)(*arg_list, val->u8);
+ break;
+ case BUILTIN_I16:
+ toc_av_add(32767)(*arg_list, val->i16);
+ break;
+ case BUILTIN_U16:
+ toc_av_add(65535)(*arg_list, val->u16);
+ break;
+ case BUILTIN_I32:
+ toc_av_add(2147483647)(*arg_list, val->i32);
+ break;
+ case BUILTIN_U32:
+ toc_av_add(4294967295)(*arg_list, val->u32);
+ break;
+ case BUILTIN_I64:
+ toc_av_add(9223372036854775807)(*arg_list, val->i64);
+ break;
+ case BUILTIN_U64:
+ toc_av_add(18446744073709551615)(*arg_list, val->u64);
+ break;
+ case BUILTIN_CHAR:
+ av_char(*arg_list, val->charv);
+ break;
+ case BUILTIN_BOOL:
+ av_uchar(*arg_list, val->boolv);
+ break;
+ case BUILTIN_PKG:
+ av_ptr(*arg_list, Package *, val->pkg);
+ break;
+ case BUILTIN_TYPE:
+ av_ptr(*arg_list, Type *, val->type);
+ break;
+ case BUILTIN_F32:
+ toc_av_f32(*arg_list, val->f32);
+ break;
+ case BUILTIN_F64:
+ toc_av_f64(*arg_list, val->f64);
+ break;
+ }
+ break;
+ case TYPE_EXPR:
+ assert(0);
+ return false;
+ }
+ return true;
+}
+
+static bool foreign_call(FnExpr *fn, Type *fn_type, Value *args, Location call_where, Value *ret) {
assert(fn->flags & FN_EXPR_FOREIGN);
- /* TODO */
+ const char *lib = fn->foreign.lib;
+ const char *name = fn->foreign.name;
+
+ /* TODO: IMPORTANT: only open libraries once */
+ void *handle = dlopen(lib, RTLD_LAZY);
+ if (!handle) {
+ err_print(call_where, "Could not open dynamic library: %s.", lib);
+ return false;
+ }
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+ void (*fn_ptr)() = dlsym(handle, name);
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+ if (!fn_ptr) {
+ err_print(call_where, "Could not get function from dynamic library: %s.", name);
+ return false;
+ }
+
+ av_alist arg_list;
+ if (!arg_list_start(&arg_list, fn_ptr, ret, &fn_type->fn.types[0], call_where))
+ return false;
+ size_t nparams = arr_len(fn_type->fn.types)-1;
+ for (size_t i = 0; i < nparams; ++i) {
+ if (!arg_list_add(&arg_list, &args[i], &fn_type->fn.types[i+1], call_where))
+ return false;
+ }
+ av_call(arg_list);
+
+ (void)fn_type; (void)args;
+
+ dlclose(handle);
+
return true;
}
#else
diff --git a/test.toc b/test.toc
index d4072af..32c08e5 100644
--- a/test.toc
+++ b/test.toc
@@ -6,16 +6,28 @@ import ::= fn(x :: []char) &Package {
};
puts :: fn(&char) i32 = #foreign "puts", "libc.so.6";
+dprintf :: fn(i32, &char, f64) i32 = #foreign "dprintf", "libc.so.6";
-hw ::= fn() int {
+hw ::= fn() i32 {
s := "Hello, world!\0";
- puts(&s[0]);
- 0
+ puts(&s[0])
+};
+
+
+sqrt :: fn(f64) f64 = #foreign "sqrt", "libm.so.6";
+
+foo ::= fn() i32 {
+ x := sqrt(2.0);
+ s := "sqrt(2) = %f\n\0";
+ dprintf(2, &s[0], x)
};
main ::= fn() {
// io ::= import("std/io");
// io.puts("Hello, world!");
- hw();
+ // hw();
x ::= hw();
+ y ::= foo();
+ sqrt2 ::= sqrt(2.0);
+ // y ::= abort_1();
}; \ No newline at end of file
diff --git a/toc.c b/toc.c
index 155e04f..0627a41 100644
--- a/toc.c
+++ b/toc.c
@@ -46,6 +46,10 @@ static void print_token(Token *t);
/* misc */
#define STRINGIFY2(x) #x
#define STRINGIFY(x) STRINGIFY2(x)
+#define join3(a,b) a##b
+#define join2(a,b) join3(a,b)
+#define join(a,b) join2(a,b)
+#define eval(x) x
static void fprint_char_literal(FILE *f, char c) {
if (isprint(c))
diff --git a/types.c b/types.c
index f2208be..5c21557 100644
--- a/types.c
+++ b/types.c
@@ -692,7 +692,7 @@ static Status type_cast_status(Type *from, Type *to) {
break;
case BUILTIN_F32:
case BUILTIN_F64:
- if (to->kind == TYPE_BUILTIN) return STATUS_ERR;
+ if (to->kind != TYPE_BUILTIN) return STATUS_ERR;
switch (to->builtin) {
case BUILTIN_I8:
case BUILTIN_U8:
@@ -1590,6 +1590,10 @@ static bool types_expr(Typer *tr, Expression *e) {
err_print(arg->where, "Expected type %s as argument to function, but got %s.", estr, gstr);
return false;
}
+ if (got->flags & TYPE_IS_FLEXIBLE) {
+ /* "cast" */
+ *got = *expected;
+ }
}
if (fn_type->constness) {