From 21efc85dba099bef731a664e67858f3cbbda62ff Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Thu, 20 Jan 2022 22:45:09 +0100 Subject: [PATCH] Convert to Lua-style stack API Only for evaluating expressions for now and right now the only exposed operation is to debug print a value on the stack, this obviously needs to be expanded. I've done this for two reasons: 1. A Lua-style stack API is much nicer to work with than to manually manage refcounts. 2. We'll soon need a more sophisticated garbage collector (if you even want to count the refcounting as garbage collection). For this, the GC will need root objects to start tracing for live objects, the stack will be one of these roots. --- src/apfl.h | 100 +----------- src/eval.c | 431 +++++++++++++++++++++++++++++++++------------------- src/main.c | 6 +- src/value.c | 1 + src/value.h | 100 ++++++++++++ 5 files changed, 388 insertions(+), 250 deletions(-) create mode 100644 src/value.h diff --git a/src/apfl.h b/src/apfl.h index 2f2e9ff..e4fc01b 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -531,112 +531,26 @@ 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; -enum apfl_value_type { - APFL_VALUE_NIL, - APFL_VALUE_BOOLEAN, - APFL_VALUE_NUMBER, - APFL_VALUE_STRING, - APFL_VALUE_LIST, - APFL_VALUE_DICT, - // TODO: functions/closures -}; +enum apfl_result { + APFL_RESULT_OK, // Evaluation succeeded, value is on the stack -struct apfl_list_data; -typedef struct apfl_list_data *apfl_list; - -struct apfl_dict_data; -typedef struct apfl_dict_data *apfl_dict; - -struct apfl_value { - enum apfl_value_type type; - union { - bool boolean; - apfl_number number; - apfl_refcounted_string string; - apfl_list list; - apfl_dict dict; - }; -}; - - -bool apfl_value_eq(const struct apfl_value, const struct apfl_value); -struct apfl_value apfl_value_move(struct apfl_value *src); -struct apfl_value apfl_value_incref(struct apfl_value); -void apfl_value_print(struct apfl_value, FILE *); -void apfl_value_deinit(struct apfl_value *); - -enum apfl_value_get_item_result { - APFL_VALUE_GET_ITEM_OK, - APFL_VALUE_GET_ITEM_KEY_DOESNT_EXIST, - APFL_VALUE_GET_ITEM_NOT_A_CONTAINER, - APFL_VALUE_GET_ITEM_WRONG_KEY_TYPE, -}; - -enum apfl_value_get_item_result apfl_value_get_item(struct apfl_value container, struct apfl_value key, struct apfl_value *out); - -apfl_list apfl_list_incref(apfl_list); -size_t apfl_list_len(apfl_list); -bool apfl_list_get_item(apfl_list, size_t index, struct apfl_value *out); -void apfl_list_unref(apfl_list); - -apfl_dict apfl_dict_incref(apfl_dict); -bool apfl_dict_get_item(apfl_dict, struct apfl_value key, struct apfl_value *out); -void apfl_dict_unref(apfl_dict); - -struct apfl_editable_list_data; -typedef struct apfl_editable_list_data *apfl_editable_list; - -apfl_editable_list apfl_editable_list_new(void); -apfl_editable_list apfl_editable_list_new_from_list(apfl_list); -bool apfl_editable_list_append(apfl_editable_list, struct apfl_value); -bool apfl_editable_list_append_list(apfl_editable_list, apfl_list); -void apfl_editable_list_destroy(apfl_editable_list); - -/* Finalize the list and return a non-editable list. - * This also destroys the editable list object, so it's no longer safe to use it. - * Returns NULL on failure - */ -apfl_list apfl_editable_list_finalize(apfl_editable_list); - -struct apfl_editable_dict_data; -typedef struct apfl_editable_dict_data *apfl_editable_dict; - -apfl_editable_dict apfl_editable_dict_new(void); -apfl_editable_dict apfl_editable_dict_new_from_dict(apfl_dict); -bool apfl_editable_dict_get(apfl_editable_dict, struct apfl_value key, struct apfl_value *out); -bool apfl_editable_dict_set(apfl_editable_dict, struct apfl_value key, struct apfl_value value); -void apfl_editable_dict_delete(apfl_editable_dict, struct apfl_value key); -void apfl_editable_dict_destroy(apfl_editable_dict); - -/* Finalize the dictionary and return a non-editable dictionary. - * This also destroys the editable dictionary object, so it's no longer safe to use it. - * Returns NULL on failure - */ -apfl_dict apfl_editable_dict_finalize(apfl_editable_dict); - -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_RESULT_ERR, + APFL_RESULT_ERR_FATAL, }; apfl_ctx apfl_ctx_new(void); void apfl_ctx_destroy(apfl_ctx); -struct apfl_result apfl_eval(apfl_ctx, struct apfl_expr); +enum apfl_result apfl_eval(apfl_ctx, struct apfl_expr); + +void apfl_debug_print_val(apfl_ctx, int, FILE *); #ifdef __cplusplus } diff --git a/src/eval.c b/src/eval.c index c142042..5c7306b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -4,9 +4,22 @@ #include "hashmap.h" #include "internal.h" #include "resizable.h" +#include "value.h" + +#define TRY(ex) \ + do { \ + enum apfl_result result = (ex); \ + if (result != APFL_RESULT_OK) { \ + return result; \ + } \ + } while (0) struct apfl_ctx_data { apfl_hashmap scope; + + struct apfl_value *stack; + size_t stack_len; + size_t stack_cap; }; struct variable_data { @@ -65,7 +78,7 @@ struct match_pattern { size_t constraints_len; }; -static struct apfl_result evaluate(apfl_ctx, struct apfl_expr *); +static enum apfl_result evaluate(apfl_ctx, struct apfl_expr *); static variable variable_new(void); static variable variable_incref(variable var); static void variable_unref(variable var); @@ -190,6 +203,110 @@ variable_set(variable var, struct apfl_value value) var->value = apfl_value_move(&value); } +static bool +stack_push(apfl_ctx ctx, struct apfl_value value) +{ + bool ok = apfl_resizable_append( + sizeof(struct apfl_value), + (void **)&ctx->stack, + &ctx->stack_len, + &ctx->stack_cap, + &value, + 1 + ); + + if (!ok) { + apfl_value_deinit(&value); + } + + return ok; +} + +static bool +stack_check_index(apfl_ctx ctx, int *index) +{ + if (*index < 0) { + if ((size_t)-*index > ctx->stack_len) { + return false; + } + *index = ctx->stack_len + *index; + } else if ((size_t)*index >= ctx->stack_len) { + return false; + } + + assert(0 <= *index && (size_t)*index < ctx->stack_len); + + return true; +} + +static bool +stack_pop(apfl_ctx ctx, struct apfl_value *value, int index) +{ + if (!stack_check_index(ctx, &index)) { + return false; + } + + *value = ctx->stack[index]; + + assert(apfl_resizable_splice( + sizeof(struct apfl_value), + (void **)ctx->stack, + &ctx->stack_len, + &ctx->stack_cap, + index, + 1, + NULL, + 0 + )); + + return true; +} + +static struct apfl_value +stack_must_pop(apfl_ctx ctx, int index) +{ + struct apfl_value value; + assert(stack_pop(ctx, &value, index)); + return value; +} + +static bool +stack_get(apfl_ctx ctx, struct apfl_value *value, int index) +{ + if (!stack_check_index(ctx, &index)) { + return false; + } + + *value = apfl_value_incref(ctx->stack[index]); + + return true; +} + +static struct apfl_value +stack_must_get(apfl_ctx ctx, int index) +{ + struct apfl_value value; + assert(stack_get(ctx, &value, index)); + return value; +} + +static bool +stack_drop(apfl_ctx ctx, int index) +{ + struct apfl_value value; + if (!stack_pop(ctx, &value, index)) { + return false; + } + apfl_value_deinit(&value); + return true; +} + +static void +stack_must_drop(apfl_ctx ctx, int index) +{ + assert(stack_drop(ctx, index)); +} + apfl_ctx apfl_ctx_new(void) { @@ -203,6 +320,10 @@ apfl_ctx_new(void) return NULL; } + ctx->stack = NULL; + ctx->stack_len = 0; + ctx->stack_cap = 0; + return ctx; } @@ -214,6 +335,11 @@ apfl_ctx_destroy(apfl_ctx ctx) } apfl_hashmap_destroy(ctx->scope); + while (ctx->stack_len > 0) { + stack_must_drop(ctx, -1); + } + free(ctx->stack); + free(ctx); } @@ -383,8 +509,9 @@ match_pattern_from_var_or_member( size_t member_keys_cap = 0; enum match_result result; - struct apfl_result eval_result; + enum apfl_result eval_result; struct apfl_value str_value; + struct apfl_value rhs_value; next: switch (var_or_member->type) { @@ -422,7 +549,7 @@ next: goto next; case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT: eval_result = evaluate(ctx, var_or_member->at.rhs); - switch (eval_result.type) { + switch (eval_result) { case APFL_RESULT_OK: break; case APFL_RESULT_ERR: @@ -433,15 +560,17 @@ next: goto fail; } + rhs_value = stack_must_pop(ctx, -1); + if (!apfl_resizable_append( sizeof(struct apfl_value), (void **)&pattern->value.member_keys, &pattern->value.member_keys_len, &member_keys_cap, - &eval_result.value, + &rhs_value, 1 )) { - apfl_value_deinit(&eval_result.value); + apfl_value_deinit(&rhs_value); result = MATCH_FATAL_ERROR; goto fail; } @@ -462,7 +591,7 @@ match_pattern_from_assignable_inner( struct apfl_expr_assignable *assignable, struct match_pattern *pattern ) { - struct apfl_result result; + struct apfl_value value; pattern->type = MPATTERN_BLANK; pattern->constraints = NULL; @@ -486,8 +615,7 @@ next: pattern->type = MPATTERN_BLANK; return MATCH_OK; case APFL_EXPR_ASSIGNABLE_PREDICATE: - result = evaluate(ctx, assignable->predicate.rhs); - switch (result.type) { + switch (evaluate(ctx, assignable->predicate.rhs)) { case APFL_RESULT_OK: break; case APFL_RESULT_ERR: @@ -496,11 +624,15 @@ next: return MATCH_FATAL_ERROR; } + if (!stack_pop(ctx, &value, -1)) { + return MATCH_FATAL_ERROR; + } + if (!match_pattern_add_constraint( pattern, &constraints_cap, MPATTERN_CONSTRAINT_PREDICATE, - apfl_value_move(&result.value) + apfl_value_move(&value) )) { return MATCH_FATAL_ERROR; } @@ -916,160 +1048,151 @@ match_pattern_apply(struct match_pattern *pattern) return MATCH_FATAL_ERROR; } -static struct apfl_result -fatal(void) +static enum apfl_result +evaluate_constant(apfl_ctx ctx, struct apfl_expr_const *constant) { - return (struct apfl_result) { .type = APFL_RESULT_ERR_FATAL }; + return stack_push(ctx, constant_to_value(constant)) + ? APFL_RESULT_OK + : APFL_RESULT_ERR_FATAL; } -static struct apfl_result -evaluate_constant(struct apfl_expr_const *constant) -{ - return (struct apfl_result) { - .type = APFL_RESULT_OK, - .value = constant_to_value(constant), - }; -} - -static struct apfl_result +static enum apfl_result evaluate_list_expr_to_list(apfl_ctx ctx, struct apfl_expr *expr, apfl_editable_list elist) { - struct apfl_result result = evaluate(ctx, expr); - if (result.type != APFL_RESULT_OK) { + enum apfl_result result = evaluate(ctx, expr); + if (result != 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_value value; + if (!stack_pop(ctx, &value, -1)) { + return APFL_RESULT_ERR_FATAL; } - apfl_list list = apfl_list_incref(result.value.list); - apfl_value_deinit(&result.value); + if (value.type != APFL_VALUE_LIST) { + apfl_value_deinit(&value); + return APFL_RESULT_ERR; + } + + apfl_list list = apfl_list_incref(value.list); + apfl_value_deinit(&value); if (!apfl_editable_list_append_list(elist, list)) { - return fatal(); + return APFL_RESULT_ERR_FATAL; } - return (struct apfl_result) { .type = APFL_RESULT_OK, .value.type = APFL_VALUE_NIL }; + return APFL_RESULT_OK; } -static struct apfl_result +static enum apfl_result evaluate_expr_list_into_list(apfl_ctx ctx, struct apfl_expr_list *list, apfl_editable_list elist) { 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, elist); - if (result.type != APFL_RESULT_OK) { - return result; - } + TRY(evaluate_list_expr_to_list(ctx, item->expr, elist)); } else { - struct apfl_result result = evaluate(ctx, item->expr); - if (result.type != APFL_RESULT_OK) { - return result; - } - - if (!apfl_editable_list_append(elist, apfl_value_move(&result.value))) { - return fatal(); + TRY(evaluate(ctx, item->expr)); + if (!apfl_editable_list_append(elist, stack_must_pop(ctx, -1))) { + return APFL_RESULT_ERR_FATAL; } } } - return (struct apfl_result) { .type = APFL_RESULT_OK, .value.type = APFL_VALUE_NIL }; + return APFL_RESULT_OK; } -static struct apfl_result +static enum apfl_result evaluate_list(apfl_ctx ctx, struct apfl_expr_list *list) { apfl_editable_list elist = apfl_editable_list_new(); if (elist == NULL) { - return fatal(); + return APFL_RESULT_ERR_FATAL; } - struct apfl_result result = evaluate_expr_list_into_list(ctx, list, elist); - if (result.type != APFL_RESULT_OK) { + enum apfl_result result = evaluate_expr_list_into_list(ctx, list, elist); + if (result != APFL_RESULT_OK) { apfl_editable_list_destroy(elist); return result; } apfl_list out = apfl_editable_list_finalize(elist); if (out == NULL) { - return fatal(); + return APFL_RESULT_ERR_FATAL; } - return (struct apfl_result) { - .type = APFL_RESULT_OK, - .value = { - .type = APFL_VALUE_LIST, - .list = out, - }, - }; + if (!stack_push(ctx, (struct apfl_value) { + .type = APFL_VALUE_LIST, + .list = out, + })) { + apfl_list_unref(out); + return APFL_RESULT_ERR_FATAL; + } + + return APFL_RESULT_OK; } -static struct apfl_result -evaluate_dict(apfl_ctx ctx, struct apfl_expr_dict *dict) -{ - apfl_editable_dict ed = apfl_editable_dict_new(); - - if (ed == NULL) { - return fatal(); - } - +static enum apfl_result +evaluate_dict_into_editable( + apfl_ctx ctx, + struct apfl_expr_dict *dict, + apfl_editable_dict ed +) { for (size_t i = 0; i < dict->len; i++) { - struct apfl_result result; - struct apfl_value key, value; + TRY(evaluate(ctx, dict->items[i].k)); + TRY(evaluate(ctx, dict->items[i].v)); - result = evaluate(ctx, dict->items[i].k); - if (result.type != APFL_RESULT_OK) { - apfl_editable_dict_destroy(ed); - return result; - } - key = result.value; - - result = evaluate(ctx, dict->items[i].v); - if (result.type != APFL_RESULT_OK) { - apfl_editable_dict_destroy(ed); - apfl_value_deinit(&key); - return result; - } - value = result.value; + struct apfl_value value = stack_must_pop(ctx, -1); + struct apfl_value key = stack_must_pop(ctx, -1); if (!apfl_editable_dict_set( ed, apfl_value_move(&key), apfl_value_move(&value) )) { - apfl_editable_dict_destroy(ed); - return fatal(); + return APFL_RESULT_ERR_FATAL; } } + return APFL_RESULT_OK; +} + +static enum apfl_result +evaluate_dict(apfl_ctx ctx, struct apfl_expr_dict *dict) +{ + apfl_editable_dict ed = apfl_editable_dict_new(); + + if (ed == NULL) { + return APFL_RESULT_ERR_FATAL; + } + + enum apfl_result result = evaluate_dict_into_editable(ctx, dict, ed); + if (result != APFL_RESULT_OK) { + apfl_editable_dict_destroy(ed); + } + apfl_dict out = apfl_editable_dict_finalize(ed); if (out == NULL) { - return fatal(); + return APFL_RESULT_ERR_FATAL; } - return (struct apfl_result) { - .type = APFL_RESULT_OK, - .value = { - .type = APFL_VALUE_DICT, - .dict = out, - }, - }; + if (!stack_push(ctx, (struct apfl_value) { + .type = APFL_VALUE_DICT, + .dict = out, + })) { + apfl_dict_unref(out); + return APFL_RESULT_ERR_FATAL; + } + + return APFL_RESULT_OK; } -static struct apfl_result +static enum apfl_result evaluate_dot(apfl_ctx ctx, struct apfl_expr_dot *dot) { - struct apfl_result result; - - result = evaluate(ctx, dot->lhs); - if (result.type != APFL_RESULT_OK) { - return result; - } - struct apfl_value lhs = result.value; + TRY(evaluate(ctx, dot->lhs)); + struct apfl_value lhs = stack_must_pop(ctx, -1); struct apfl_value key = (struct apfl_value) { .type = APFL_VALUE_STRING, @@ -1077,52 +1200,42 @@ evaluate_dot(apfl_ctx ctx, struct apfl_expr_dot *dot) }; struct apfl_value out; - if (apfl_value_get_item(apfl_value_move(&lhs), apfl_value_move(&key), &out) == APFL_VALUE_GET_ITEM_OK) { - return (struct apfl_result) { - .type = APFL_RESULT_OK, - .value = out, - }; - } else { - return (struct apfl_result) { .type = APFL_RESULT_ERR }; // TODO: Describe error + if (apfl_value_get_item(apfl_value_move(&lhs), apfl_value_move(&key), &out) != APFL_VALUE_GET_ITEM_OK) { + return APFL_RESULT_ERR; // TODO: Describe error } + + return stack_push(ctx, out) + ? APFL_RESULT_OK + : APFL_RESULT_ERR_FATAL; } -static struct apfl_result +static enum apfl_result evaluate_at(apfl_ctx ctx, struct apfl_expr_at *at) { - struct apfl_result result; - result = evaluate(ctx, at->lhs); - if (result.type != APFL_RESULT_OK) { - return result; - } - struct apfl_value lhs = result.value; + TRY(evaluate(ctx, at->lhs)); + TRY(evaluate(ctx, at->rhs)); - result = evaluate(ctx, at->rhs); - if (result.type != APFL_RESULT_OK) { - apfl_value_deinit(&lhs); - return result; - } - struct apfl_value rhs = result.value; + struct apfl_value rhs = stack_must_pop(ctx, -1); + struct apfl_value lhs = stack_must_pop(ctx, -1); struct apfl_value out; - if (apfl_value_get_item(apfl_value_move(&lhs), apfl_value_move(&rhs), &out) == APFL_VALUE_GET_ITEM_OK) { - return (struct apfl_result) { - .type = APFL_RESULT_OK, - .value = out, - }; - } else { - return (struct apfl_result) { .type = APFL_RESULT_ERR }; // TODO: Describe error + if (apfl_value_get_item(apfl_value_move(&lhs), apfl_value_move(&rhs), &out) != APFL_VALUE_GET_ITEM_OK) { + return APFL_RESULT_ERR; // TODO: Describe error } + + return stack_push(ctx, out) + ? APFL_RESULT_OK + : APFL_RESULT_ERR_FATAL; } -static struct apfl_result +static enum apfl_result failing_match_result_to_apfl_result(enum match_result match_result) { if (match_result == MATCH_FATAL_ERROR) { - return fatal(); + return APFL_RESULT_ERR_FATAL; } - return (struct apfl_result) { .type = APFL_RESULT_ERR }; + return APFL_RESULT_ERR; } static enum match_result @@ -1167,7 +1280,7 @@ evaluate_assignment_finish( } -static struct apfl_result +static enum apfl_result evaluate_assignment(apfl_ctx ctx, struct apfl_expr_assignment *assignment) { struct match_pattern pattern; @@ -1181,48 +1294,48 @@ evaluate_assignment(apfl_ctx ctx, struct apfl_expr_assignment *assignment) return failing_match_result_to_apfl_result(match_result); } - struct apfl_result result = evaluate(ctx, assignment->rhs); - if (result.type != APFL_RESULT_OK) { + enum apfl_result result = evaluate(ctx, assignment->rhs); + if (result != APFL_RESULT_OK) { match_pattern_deinit(&pattern); return result; } match_result = evaluate_assignment_finish( &pattern, - apfl_value_incref(result.value) + stack_must_get(ctx, -1) // stack_must_get instead of stack_must_pop, + // so the value is stull on the stack on return. ); match_pattern_deinit(&pattern); if (match_result != MATCH_OK) { - apfl_value_deinit(&result.value); + stack_must_drop(ctx, -1); return failing_match_result_to_apfl_result(match_result); } - return result; + return APFL_RESULT_OK; } -static struct apfl_result +static enum apfl_result evaluate_var(apfl_ctx ctx, apfl_refcounted_string varname) { variable var = ctx_get_var(ctx, varname); if (var == NULL) { - return (struct apfl_result) { .type = APFL_RESULT_ERR }; + return APFL_RESULT_ERR; } - struct apfl_value value = apfl_value_incref(var->value); + bool ok = stack_push(ctx, apfl_value_incref(var->value)); variable_unref(var); - return (struct apfl_result) { - .type = APFL_RESULT_OK, - .value = value, - }; + return ok + ? APFL_RESULT_OK + : APFL_RESULT_ERR_FATAL; } -static struct apfl_result +static enum apfl_result evaluate(apfl_ctx ctx, struct apfl_expr *expr) { switch (expr->type) { case APFL_EXPR_CONSTANT: - return evaluate_constant(&expr->constant); + return evaluate_constant(ctx, &expr->constant); case APFL_EXPR_LIST: return evaluate_list(ctx, &expr->list); case APFL_EXPR_DICT: @@ -1236,25 +1349,37 @@ evaluate(apfl_ctx ctx, struct apfl_expr *expr) case APFL_EXPR_VAR: return evaluate_var(ctx, apfl_refcounted_string_incref(expr->var)); case APFL_EXPR_BLANK: - return (struct apfl_result) { - .type = APFL_RESULT_OK, - .value = { - .type = APFL_VALUE_NIL, - }, - }; + return stack_push(ctx, (struct apfl_value) { + .type = APFL_VALUE_NIL, + }) + ? APFL_RESULT_OK + : APFL_RESULT_ERR_FATAL; case APFL_EXPR_CALL: case APFL_EXPR_SIMPLE_FUNC: case APFL_EXPR_COMPLEX_FUNC: break; // Not implemented yet } - return (struct apfl_result) { .type = APFL_RESULT_ERR }; + return APFL_RESULT_ERR; } -struct apfl_result +enum apfl_result apfl_eval(apfl_ctx ctx, struct apfl_expr expr) { - struct apfl_result result = evaluate(ctx, &expr); + enum apfl_result result = evaluate(ctx, &expr); apfl_expr_deinit(&expr); return result; } + +void +apfl_debug_print_val(apfl_ctx ctx, int index, FILE *f) +{ + struct apfl_value value; + if (!stack_pop(ctx, &value, index)) { + fprintf(f, "apfl_debug_print_val: Invalid stack index %d\n", index); + return; + } + + apfl_value_print(value, f); + apfl_value_deinit(&value); +} diff --git a/src/main.c b/src/main.c index 9855d2a..6507a81 100644 --- a/src/main.c +++ b/src/main.c @@ -106,11 +106,9 @@ repl_eval(apfl_parser_ptr parser, apfl_ctx ctx) struct apfl_expr expr; int rv; while (repl_parser_generic(parser, &expr, &rv)) { - struct apfl_result result = apfl_eval(ctx, expr); - switch (result.type) { + switch (apfl_eval(ctx, expr)) { case APFL_RESULT_OK: - apfl_value_print(result.value, stdout); - apfl_value_deinit(&result.value); + apfl_debug_print_val(ctx, -1, stdout); break; case APFL_RESULT_ERR: fprintf(stderr, "Error occurred during evaluation.\n"); diff --git a/src/value.c b/src/value.c index 61ba4b7..045fd80 100644 --- a/src/value.c +++ b/src/value.c @@ -4,6 +4,7 @@ #include "internal.h" #include "resizable.h" #include "hashmap.h" +#include "value.h" struct apfl_list_data { unsigned refcount; diff --git a/src/value.h b/src/value.h new file mode 100644 index 0000000..e00eb99 --- /dev/null +++ b/src/value.h @@ -0,0 +1,100 @@ +#ifndef APFL_VALUE_H +#define APFL_VALUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "apfl.h" + +enum apfl_value_type { + APFL_VALUE_NIL, + APFL_VALUE_BOOLEAN, + APFL_VALUE_NUMBER, + APFL_VALUE_STRING, + APFL_VALUE_LIST, + APFL_VALUE_DICT, + // TODO: functions/closures +}; + +struct apfl_list_data; +typedef struct apfl_list_data *apfl_list; + +struct apfl_dict_data; +typedef struct apfl_dict_data *apfl_dict; + +struct apfl_value { + enum apfl_value_type type; + union { + bool boolean; + apfl_number number; + apfl_refcounted_string string; + apfl_list list; + apfl_dict dict; + }; +}; + + +bool apfl_value_eq(const struct apfl_value, const struct apfl_value); +struct apfl_value apfl_value_move(struct apfl_value *src); +struct apfl_value apfl_value_incref(struct apfl_value); +void apfl_value_print(struct apfl_value, FILE *); +void apfl_value_deinit(struct apfl_value *); + +enum apfl_value_get_item_result { + APFL_VALUE_GET_ITEM_OK, + APFL_VALUE_GET_ITEM_KEY_DOESNT_EXIST, + APFL_VALUE_GET_ITEM_NOT_A_CONTAINER, + APFL_VALUE_GET_ITEM_WRONG_KEY_TYPE, +}; + +enum apfl_value_get_item_result apfl_value_get_item(struct apfl_value container, struct apfl_value key, struct apfl_value *out); + +apfl_list apfl_list_incref(apfl_list); +size_t apfl_list_len(apfl_list); +bool apfl_list_get_item(apfl_list, size_t index, struct apfl_value *out); +void apfl_list_unref(apfl_list); + +apfl_dict apfl_dict_incref(apfl_dict); +bool apfl_dict_get_item(apfl_dict, struct apfl_value key, struct apfl_value *out); +void apfl_dict_unref(apfl_dict); + +struct apfl_editable_list_data; +typedef struct apfl_editable_list_data *apfl_editable_list; + +apfl_editable_list apfl_editable_list_new(void); +apfl_editable_list apfl_editable_list_new_from_list(apfl_list); +bool apfl_editable_list_append(apfl_editable_list, struct apfl_value); +bool apfl_editable_list_append_list(apfl_editable_list, apfl_list); +void apfl_editable_list_destroy(apfl_editable_list); + +/* Finalize the list and return a non-editable list. + * This also destroys the editable list object, so it's no longer safe to use it. + * Returns NULL on failure + */ +apfl_list apfl_editable_list_finalize(apfl_editable_list); + +struct apfl_editable_dict_data; +typedef struct apfl_editable_dict_data *apfl_editable_dict; + +apfl_editable_dict apfl_editable_dict_new(void); +apfl_editable_dict apfl_editable_dict_new_from_dict(apfl_dict); +bool apfl_editable_dict_get(apfl_editable_dict, struct apfl_value key, struct apfl_value *out); +bool apfl_editable_dict_set(apfl_editable_dict, struct apfl_value key, struct apfl_value value); +void apfl_editable_dict_delete(apfl_editable_dict, struct apfl_value key); +void apfl_editable_dict_destroy(apfl_editable_dict); + +/* Finalize the dictionary and return a non-editable dictionary. + * This also destroys the editable dictionary object, so it's no longer safe to use it. + * Returns NULL on failure + */ +apfl_dict apfl_editable_dict_finalize(apfl_editable_dict); + +#ifdef __cplusplus +} +#endif + +#endif