diff options
author | Leo Tenenbaum <pommicket@gmail.com> | 2020-01-19 14:44:11 -0500 |
---|---|---|
committer | Leo Tenenbaum <pommicket@gmail.com> | 2020-01-19 14:44:11 -0500 |
commit | 304a43226ce9f44dc98d87d2b9ca0316258d6035 (patch) | |
tree | e2f083acc40e00019f822725e03372d89e98c741 | |
parent | 13ff2041474f75647b34c5bec58e3289e56be8df (diff) |
#foreign is kinda working now!
-rwxr-xr-x | build.sh | 2 | ||||
-rw-r--r-- | eval.c | 2 | ||||
-rw-r--r-- | foreign.c | 260 | ||||
-rw-r--r-- | test.toc | 20 | ||||
-rw-r--r-- | toc.c | 4 | ||||
-rw-r--r-- | types.c | 6 |
6 files changed, 285 insertions, 9 deletions
@@ -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 @@ -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; @@ -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 @@ -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 @@ -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)) @@ -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) { |