From 60d121e290ddefa02d9ff8e1eeef772aa69c7742 Mon Sep 17 00:00:00 2001 From: Aymeric Wibo Date: Fri, 13 Sep 2024 20:34:23 +0200 Subject: [PATCH] external functions: Ability to pass arguments to functions --- flamingo/common.h | 32 +------------------------------ flamingo/flamingo.c | 32 +++++++++++++++++++++++++++++++ flamingo/flamingo.h | 25 +++++++++++++++++++++++- flamingo/grammar/call.h | 30 ++++++++++++++++++++++++++--- flamingo/val.c | 12 ++++++++++++ main.c | 42 +++++++++++++++++++++++++++++++++-------- tests/proto.fl | 17 ++++++++++++----- 7 files changed, 142 insertions(+), 48 deletions(-) diff --git a/flamingo/common.h b/flamingo/common.h index a38f26e..ffe247d 100644 --- a/flamingo/common.h +++ b/flamingo/common.h @@ -29,34 +29,4 @@ static inline int parse_assignment(flamingo_t* flamingo, TSNode node); static inline int parse_import(flamingo_t* flamingo, TSNode node); static inline int parse_function_declaration(flamingo_t* flamingo, TSNode node, flamingo_fn_kind_t kind); -__attribute__((format(printf, 2, 3))) static inline int error(flamingo_t* flamingo, char const* fmt, ...) { - va_list args; - va_start(args, fmt); - - // TODO validate the size of the program name - - // format caller's error message - - char formatted[sizeof flamingo->err]; - size_t res = vsnprintf(formatted, sizeof formatted, fmt, args); - assert(res < sizeof formatted); - - // format the new error message and concatenate to the previous one if there were still errors outstanding - // TODO truncate the number of errors that we show at once (just do this by seeing how much longer we have) - - if (flamingo->errors_outstanding) { - size_t const prev_len = strlen(flamingo->err); - res = snprintf(flamingo->err + prev_len, sizeof flamingo->err - prev_len, ", %s:%d:%d: %s", flamingo->progname, 0, 0, formatted); - } - - else { - res = snprintf(flamingo->err, sizeof flamingo->err, "%s:%d:%d: %s", flamingo->progname, 0, 0, formatted); - } - - assert(res < sizeof flamingo->err); - - va_end(args); - flamingo->errors_outstanding = true; - - return -1; -} +#define error(...) (flamingo_raise_error(__VA_ARGS__)) diff --git a/flamingo/flamingo.c b/flamingo/flamingo.c index 65559f3..4273750 100644 --- a/flamingo/flamingo.c +++ b/flamingo/flamingo.c @@ -22,6 +22,38 @@ typedef struct { extern TSLanguage const* tree_sitter_flamingo(void); +__attribute__((format(printf, 2, 3))) int flamingo_raise_error(flamingo_t* flamingo, char const* fmt, ...) { + va_list args; + va_start(args, fmt); + + // TODO validate the size of the program name + + // format caller's error message + + char formatted[sizeof flamingo->err]; + size_t res = vsnprintf(formatted, sizeof formatted, fmt, args); + assert(res < sizeof formatted); + + // format the new error message and concatenate to the previous one if there were still errors outstanding + // TODO truncate the number of errors that we show at once (just do this by seeing how much longer we have) + + if (flamingo->errors_outstanding) { + size_t const prev_len = strlen(flamingo->err); + res = snprintf(flamingo->err + prev_len, sizeof flamingo->err - prev_len, ", %s:%d:%d: %s", flamingo->progname, 0, 0, formatted); + } + + else { + res = snprintf(flamingo->err, sizeof flamingo->err, "%s:%d:%d: %s", flamingo->progname, 0, 0, formatted); + } + + assert(res < sizeof flamingo->err); + + va_end(args); + flamingo->errors_outstanding = true; + + return -1; +} + int flamingo_create(flamingo_t* flamingo, char const* progname, char* src, size_t src_size) { flamingo->consistent = false; diff --git a/flamingo/flamingo.h b/flamingo/flamingo.h index c444e9c..4d56afe 100644 --- a/flamingo/flamingo.h +++ b/flamingo/flamingo.h @@ -6,13 +6,15 @@ #include #include #include +#include typedef struct flamingo_t flamingo_t; typedef struct flamingo_val_t flamingo_val_t; typedef struct flamingo_var_t flamingo_var_t; typedef struct flamingo_scope_t flamingo_scope_t; +typedef struct flamingo_arg_list_t flamingo_arg_list_t; -typedef int (*flamingo_external_fn_cb_t)(flamingo_t* flamingo, size_t name_size, char* name, void* data, size_t arg_count, flamingo_val_t** args, flamingo_val_t** rv); +typedef int (*flamingo_external_fn_cb_t)(flamingo_t* flamingo, size_t name_size, char* name, void* data, flamingo_arg_list_t* args, flamingo_val_t** rv); typedef enum { FLAMINGO_VAL_KIND_NONE, @@ -94,6 +96,11 @@ struct flamingo_scope_t { bool class_scope; }; +struct flamingo_arg_list_t { + size_t count; + flamingo_val_t** args; +}; + struct flamingo_t { char const* progname; bool consistent; // Set if we managed to create the instance, so we know whether or not it still needs freeing. @@ -129,6 +136,8 @@ struct flamingo_t { flamingo_val_t* cur_fn_rv; }; +__attribute__((format(printf, 2, 3))) int flamingo_raise_error(flamingo_t* flamingo, char const* fmt, ...); + int flamingo_create(flamingo_t* flamingo, char const* progname, char* src, size_t src_size); void flamingo_destroy(flamingo_t* flamingo); @@ -144,3 +153,17 @@ flamingo_val_t* flamingo_val_make_int(int64_t integer); flamingo_val_t* flamingo_val_make_str(size_t size, char* str); flamingo_val_t* flamingo_val_make_cstr(char* str); flamingo_val_t* flamingo_val_make_bool(bool boolean); + +flamingo_val_t* flamingo_val_find_arg(flamingo_arg_list_t* args, char const* name); + +static inline int flamingo_strcmp(char* a, char* b, size_t a_size, size_t b_size) { + if (a_size != b_size) { + return -1; // XXX Not right but whatever. + } + + return memcmp(a, b, a_size); +} + +static inline int flamingo_cstrcmp(char* str, char* cstr, size_t str_size) { + return flamingo_strcmp(str, cstr, str_size, strlen(cstr)); +} diff --git a/flamingo/grammar/call.h b/flamingo/grammar/call.h index 7449bb1..7c8af7e 100644 --- a/flamingo/grammar/call.h +++ b/flamingo/grammar/call.h @@ -183,13 +183,37 @@ static int parse_call(flamingo_t* flamingo, TSNode node, flamingo_val_t** val) { return error(flamingo, "cannot call external function without a external function callback being set"); } - size_t arg_count = 0; - flamingo_val_t** args = NULL; + // Create arg list. + + flamingo_scope_t* const arg_scope = flamingo->scope_stack[flamingo->scope_stack_size - 1]; + + size_t const arg_count = arg_scope->vars_size; + flamingo_val_t** const args = malloc(arg_count * sizeof *args); + assert(args != NULL); + + for (size_t i = 0; i < arg_count; i++) { + args[i] = arg_scope->vars[i].val; + + // TODO Should I have to do this? + + args[i]->name_size = arg_scope->vars[i].key_size; + args[i]->name = arg_scope->vars[i].key; + } + + flamingo_arg_list_t arg_list = { + .count = arg_count, + .args = args, + }; + + // Actually call the external function callback. + assert(flamingo->cur_fn_rv == NULL); - if (flamingo->external_fn_cb(flamingo, callable->name_size, callable->name, flamingo->external_fn_cb_data, arg_count, args, &flamingo->cur_fn_rv) < 0) { + if (flamingo->external_fn_cb(flamingo, callable->name_size, callable->name, flamingo->external_fn_cb_data, &arg_list, &flamingo->cur_fn_rv) < 0) { return -1; } + + free(args); } else if (parse_block(flamingo, *body, is_class ? &inner_scope : NULL) < 0) { diff --git a/flamingo/val.c b/flamingo/val.c index bb98974..530d6a4 100644 --- a/flamingo/val.c +++ b/flamingo/val.c @@ -157,3 +157,15 @@ flamingo_val_t* flamingo_val_make_bool(bool boolean) { return val; } + +flamingo_val_t* flamingo_val_find_arg(flamingo_arg_list_t* args, char const* name) { + for (size_t i = 0; i < args->count; i++) { + flamingo_val_t* const arg = args->args[i]; + + if (flamingo_cstrcmp(arg->name, (char*) name, arg->name_size) == 0) { + return arg; + } + } + + return NULL; +} diff --git a/main.c b/main.c index 05d323d..2e70337 100644 --- a/main.c +++ b/main.c @@ -36,29 +36,55 @@ static void usage(void) { exit(EXIT_FAILURE); } -static int external_fn_cb(flamingo_t* flamingo, size_t name_size, char* name, void* data, size_t arg_count, flamingo_val_t** args, flamingo_val_t** rv) { - if (memcmp(name, "test_return_number", name_size) == 0) { +static int external_fn_cb(flamingo_t* flamingo, size_t name_size, char* name, void* data, flamingo_arg_list_t* args, flamingo_val_t** rv) { + if (flamingo_cstrcmp(name, "test_return_number", name_size) == 0) { *rv = flamingo_val_make_int(420); } - else if (memcmp(name, "test_return_bool", name_size) == 0) { + else if (flamingo_cstrcmp(name, "test_return_bool", name_size) == 0) { *rv = flamingo_val_make_bool(true); } - else if (memcmp(name, "test_return_str", name_size) == 0) { + else if (flamingo_cstrcmp(name, "test_return_str", name_size) == 0) { *rv = flamingo_val_make_cstr("zonnebloemgranen"); } - else if (memcmp(name, "test_return_none", name_size) == 0) { + else if (flamingo_cstrcmp(name, "test_return_none", name_size) == 0) { *rv = flamingo_val_make_none(); } - else if (memcmp(name, "test_do_literally_nothing", name_size) == 0) { + else if (flamingo_cstrcmp(name, "test_do_literally_nothing", name_size) == 0) { + } + + else if (flamingo_cstrcmp(name, "test_sub", name_size) == 0) { + if (args->count != 2) { + return flamingo_raise_error(flamingo, "test_sub: expected 2 arguments, got %zu", args->count); + } + + flamingo_val_t* const a = flamingo_val_find_arg(args, "a"); + flamingo_val_t* const b = flamingo_val_find_arg(args, "b"); + + if (a == NULL) { + return flamingo_raise_error(flamingo, "test_sub: argument 'a' doesn't exist"); + } + + if (b == NULL) { + return flamingo_raise_error(flamingo, "test_sub: argument 'b' doesn't exist"); + } + + if (a->kind != FLAMINGO_VAL_KIND_INT) { + return flamingo_raise_error(flamingo, "test_sub: expected 'a' to be an integer"); + } + + if (b->kind != FLAMINGO_VAL_KIND_INT) { + return flamingo_raise_error(flamingo, "test_sub: expected 'b' to be an integer"); + } + + *rv = flamingo_val_make_int(a->integer.integer - b->integer.integer); } else { - printf("Runtime does not support the '%.*s' external function call (%zu args)\n", (int) name_size, name, arg_count); - return -1; + return flamingo_raise_error(flamingo, "runtime does not support the '%.*s' external function call (%zu arguments passed)", (int) name_size, name, args->count); } return 0; diff --git a/tests/proto.fl b/tests/proto.fl index 77b687e..164760f 100644 --- a/tests/proto.fl +++ b/tests/proto.fl @@ -1,14 +1,21 @@ -proto test_do_literally_nothing +# Test external function returns. + +proto test_do_literally_nothing -> none assert test_do_literally_nothing() == none -proto test_return_number +proto test_return_number -> int assert test_return_number() == 420 -proto test_return_bool +proto test_return_bool -> bool assert test_return_bool() == true -proto test_return_str +proto test_return_str -> str assert test_return_str() == "zonnebloemgranen" -proto test_return_none +proto test_return_none -> none assert test_return_none() == none + +# Test external function arguments. + +proto test_sub(a: int, b: int) -> int +assert test_sub(420, 69) == 420 - 69