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