diff --git a/src/Makefile.am b/src/Makefile.am index c6bc49b..f6bba9f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,6 +4,7 @@ lib_LIBRARIES = libapfl.a libapfl_a_SOURCES = libapfl_a_SOURCES += error.c +libapfl_a_SOURCES += eval.c libapfl_a_SOURCES += expr.c libapfl_a_SOURCES += internal.c libapfl_a_SOURCES += position.c @@ -13,6 +14,7 @@ libapfl_a_SOURCES += token.c libapfl_a_SOURCES += tokenizer.c libapfl_a_SOURCES += parser.c libapfl_a_SOURCES += source_readers.c +libapfl_a_SOURCES += value.c apfl_internal_headers = apfl_internal_headers += common.h diff --git a/src/apfl.h b/src/apfl.h index a27557f..b279fa8 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -473,6 +473,83 @@ struct apfl_error apfl_parser_get_error(apfl_parser_ptr); */ struct apfl_expr apfl_parser_get_expr(apfl_parser_ptr); + +struct apfl_ctx_data; +typedef struct apfl_ctx_data *apfl_ctx; + +typedef unsigned apfl_refcount; + +struct apfl_list { + // TODO: Not sure if it's a good idea to expose this struct? + + apfl_refcount refcount; + struct apfl_value *items; + size_t len; + size_t cap; +}; + + +enum apfl_value_type { + APFL_VALUE_NIL, + APFL_VALUE_BOOLEAN, + APFL_VALUE_NUMBER, + APFL_VALUE_STRING, + APFL_VALUE_LIST, + // TODO: dict, functions/closures +}; + +struct apfl_value { + enum apfl_value_type type; + union { + bool boolean; + apfl_number number; + struct apfl_string string; + struct apfl_list *list; + }; +}; + +bool apfl_value_copy(struct apfl_value *dst, struct apfl_value src); +void apfl_value_print(struct apfl_value, FILE *); +void apfl_value_deinit(struct apfl_value *); + +enum apfl_function_response_type { + APFL_FUNCTION_RESPONSE_OK, + APFL_FUNCTION_RESPONSE_ERROR, +}; + +struct apfl_function_response { + enum apfl_function_response_type type; + struct apfl_value value; +}; + +typedef struct apfl_function_response (*apfl_user_function_cb)(apfl_ctx, struct apfl_list arguments, void *); + +struct apfl_user_function { + apfl_user_function_cb cb; + void *opaque; +}; + + +enum apfl_result_type { + APFL_RESULT_OK, + APFL_RESULT_ERR, + APFL_RESULT_ERR_FATAL, +}; + +struct apfl_result { + enum apfl_result_type type; + struct apfl_value value; + // TODO: No further details are yet provided on (fatal) error. Not quite + // sure what to return / how errors/exceptions should work. Maybe a + // value + a backtrace? +}; + +apfl_ctx apfl_ctx_new(void); + +void apfl_ctx_destroy(apfl_ctx); + +struct apfl_result apfl_eval(apfl_ctx, struct apfl_expr); + #ifdef __cplusplus } #endif diff --git a/src/eval.c b/src/eval.c new file mode 100644 index 0000000..a71cc88 --- /dev/null +++ b/src/eval.c @@ -0,0 +1,188 @@ +#include + +#include "apfl.h" +#include "internal.h" +#include "resizable.h" + +struct apfl_ctx_data { + char nothing_here_yet; +}; + +static struct apfl_result evaluate(apfl_ctx, struct apfl_expr *); + +apfl_ctx +apfl_ctx_new() +{ + apfl_ctx ctx = ALLOC(struct apfl_ctx_data); + if (ctx == NULL) { + return NULL; + } + + return ctx; +} + +void +apfl_ctx_destroy(apfl_ctx ctx) +{ + free(ctx); +} + +static struct apfl_value +evaluate_constant(struct apfl_expr_const constant) +{ + struct apfl_string str = apfl_string_blank(); + + switch (constant.type) { + case APFL_EXPR_CONST_NIL: + return (struct apfl_value) { + .type = APFL_VALUE_NIL, + }; + case APFL_EXPR_CONST_BOOLEAN: + return (struct apfl_value) { + .type = APFL_VALUE_BOOLEAN, + .boolean = constant.boolean, + }; + case APFL_EXPR_CONST_STRING: + assert(apfl_string_copy(&str, apfl_string_view_from(constant.string))); // TODO: This can fail! + return (struct apfl_value) { + .type = APFL_VALUE_STRING, + .string = str, + }; + case APFL_EXPR_CONST_NUMBER: + return (struct apfl_value) { + .type = APFL_VALUE_NUMBER, + .number = constant.number, + }; + } + + assert(false); +} + +static struct apfl_result +evaluate_list_expr_to_list(apfl_ctx ctx, struct apfl_expr *expr, struct apfl_list *out) +{ + struct apfl_result result = evaluate(ctx, expr); + if (result.type != APFL_RESULT_OK) { + return result; + } + + if (result.value.type != APFL_VALUE_LIST) { + apfl_value_deinit(&result.value); + return (struct apfl_result) { .type = APFL_RESULT_ERR }; + } + + struct apfl_list *list = result.value.list; + + if (!apfl_resizable_ensure_cap_for_more_elements( + sizeof(struct apfl_value), + (void **)&out->items, + out->len, + &out->cap, + list->len + )) { + apfl_value_deinit(&result.value); + return (struct apfl_result) { .type = APFL_RESULT_ERR_FATAL }; + } + + for (size_t i = 0; i < list->len; i++) { + if (!apfl_value_copy(&out->items[out->len], list->items[i])) { + apfl_value_deinit(&result.value); + return (struct apfl_result) { .type = APFL_RESULT_ERR_FATAL }; + } + out->len++; + } + + apfl_value_deinit(&result.value); + return (struct apfl_result) { .type = APFL_RESULT_OK, .value.type = APFL_VALUE_NIL }; +} + +static struct apfl_result +evaluate_expr_list_into_list(apfl_ctx ctx, struct apfl_expr_list *list, struct apfl_list *out) +{ + for (size_t i = 0; i < list->len; i++) { + struct apfl_expr_list_item *item = &list->items[i]; + + if (item->expand) { + struct apfl_result result = evaluate_list_expr_to_list(ctx, item->expr, out); + if (result.type != APFL_RESULT_OK) { + return result; + } + } else { + struct apfl_result result = evaluate(ctx, item->expr); + if (result.type != APFL_RESULT_OK) { + return result; + } + + if (!apfl_resizable_append( + sizeof(struct apfl_value), + (void **)&out->items, + &out->len, + &out->cap, + &result.value, + 1 + )) { + apfl_value_deinit(&result.value); + return (struct apfl_result) { .type = APFL_RESULT_ERR_FATAL }; + } + } + } + + return (struct apfl_result) { .type = APFL_RESULT_OK, .value.type = APFL_VALUE_NIL }; +} + +static struct apfl_result +evaluate_list(apfl_ctx ctx, struct apfl_expr_list *list) +{ + struct apfl_list *out = apfl_list_new(); + if (out == NULL) { + return (struct apfl_result) { .type = APFL_RESULT_ERR_FATAL }; + } + + struct apfl_result result = evaluate_expr_list_into_list(ctx, list, out); + if (result.type != APFL_RESULT_OK) { + DESTROY(out, apfl_list_deinit); + return result; + } + + return (struct apfl_result) { + .type = APFL_RESULT_OK, + .value = { + .type = APFL_VALUE_LIST, + .list = out, + }, + }; +} + +static struct apfl_result +evaluate(apfl_ctx ctx, struct apfl_expr *expr) +{ + (void)ctx; + switch (expr->type) { + case APFL_EXPR_CONSTANT: + return (struct apfl_result) { + .type = APFL_RESULT_OK, + .value = evaluate_constant(expr->constant), + }; + case APFL_EXPR_LIST: + return evaluate_list(ctx, &expr->list); + case APFL_EXPR_VAR: + case APFL_EXPR_CALL: + case APFL_EXPR_DICT: + case APFL_EXPR_SIMPLE_FUNC: + case APFL_EXPR_COMPLEX_FUNC: + case APFL_EXPR_ASSIGNMENT: + case APFL_EXPR_DOT: + case APFL_EXPR_AT: + break; // Not implemented yet + } + + return (struct apfl_result) { .type = APFL_RESULT_ERR }; +} + +struct apfl_result +apfl_eval(apfl_ctx ctx, struct apfl_expr expr) +{ + struct apfl_result result = evaluate(ctx, &expr); + apfl_expr_deinit(&expr); + return result; +} diff --git a/src/internal.h b/src/internal.h index da40a92..aa3cb9b 100644 --- a/src/internal.h +++ b/src/internal.h @@ -45,6 +45,9 @@ extern "C" { // Internal use only functions void apfl_print_indented(unsigned indent, FILE *, const char* fmt, ...); +struct apfl_list *apfl_list_new(void); +void apfl_list_deinit(struct apfl_list *); + #ifdef __cplusplus } #endif diff --git a/src/value.c b/src/value.c new file mode 100644 index 0000000..c136516 --- /dev/null +++ b/src/value.c @@ -0,0 +1,133 @@ +#include + +#include "apfl.h" +#include "internal.h" + +/** + * Decrease a refererence count. Returns true, if the object should be freed. + */ +static bool +dec_refcount(unsigned *rc) +{ + if (rc == 0) { + return false; + } + return --(*rc) == 0; +} + +static void +print(unsigned indent, FILE *out, struct apfl_value value) +{ + switch (value.type) { + case APFL_VALUE_NIL: + apfl_print_indented(indent, out, "nil\n"); + return; + case APFL_VALUE_BOOLEAN: + apfl_print_indented(indent, out, value.boolean ? "true\n" : "false\n"); + return; + case APFL_VALUE_NUMBER: + apfl_print_indented(indent, out, "%f\n", value.number); + return; + case APFL_VALUE_STRING: + apfl_print_indented(indent, out, "\"" APFL_STR_FMT "\"\n", APFL_STR_FMT_ARGS(value.string)); + return; + case APFL_VALUE_LIST: + apfl_print_indented(indent, out, "[\n"); + for (size_t i = 0; i < value.list->len; i++) { + print(indent+1, out, value.list->items[i]); + } + apfl_print_indented(indent, out, "]\n"); + return; + } + + fprintf(out, "Unknown value? (%d)\n", (int)value.type); +} + +bool +apfl_value_copy(struct apfl_value *dst, struct apfl_value src) +{ + struct apfl_string string; + + *dst = src; + + switch (src.type) { + case APFL_VALUE_NIL: + case APFL_VALUE_BOOLEAN: + case APFL_VALUE_NUMBER: + // Nothing to do + goto ok; + case APFL_VALUE_STRING: + string = apfl_string_blank(); + if (!apfl_string_copy(&string, apfl_string_view_from(src.string))) { + return false; + } + dst->string = string; + goto ok; + case APFL_VALUE_LIST: + assert(src.list != NULL); + assert(dst->list == src.list); + src.list->refcount++; + goto ok; + } + + assert(false); + +ok: + return true; +} + +void +apfl_value_print(struct apfl_value value, FILE *out) +{ + print(0, out, value); +} + +struct apfl_list * +apfl_list_new(void) +{ + struct apfl_list *list = ALLOC(struct apfl_list); + if (list == NULL) { + return NULL; + } + + *list = (struct apfl_list) { + .refcount = 1, + .items = NULL, + .len = 0, + .cap = 0, + }; + + return list; +} + +void +apfl_list_deinit(struct apfl_list *list) +{ + DEINIT_LIST(list->items, list->len, apfl_value_deinit); + list->refcount = 0; + list->cap = 0; +} + +void +apfl_value_deinit(struct apfl_value *value) +{ + switch (value->type) { + case APFL_VALUE_NIL: + case APFL_VALUE_BOOLEAN: + case APFL_VALUE_NUMBER: + goto ok; + case APFL_VALUE_STRING: + apfl_string_deinit(&value->string); + goto ok; + case APFL_VALUE_LIST: + if (dec_refcount(&value->list->refcount)) { + DESTROY(value->list, apfl_list_deinit); + } + goto ok; + } + + assert(false); + +ok: + value->type = APFL_VALUE_NIL; +} diff --git a/src/value.h b/src/value.h deleted file mode 100644 index e69de29..0000000