From e928f40da4d6c4f1cfecc393e5d73f5a7b66e5ce Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Thu, 6 Jan 2022 22:53:26 +0100 Subject: [PATCH] Implement simple variable support We can now assign to variables and retrieve their values. Not all assignables are implemented yet, but still pretty cool :). --- src/eval.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 219 insertions(+), 3 deletions(-) diff --git a/src/eval.c b/src/eval.c index 12b3fd1..954cfe9 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1,32 +1,192 @@ #include #include "apfl.h" +#include "hashmap.h" #include "internal.h" #include "resizable.h" struct apfl_ctx_data { - char nothing_here_yet; + apfl_hashmap scope; }; +struct variable_data { + unsigned refcount; + struct apfl_value value; +}; + +typedef struct variable_data *variable; + static struct 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); + +static bool +scope_keys_eq(void *opaque, const void *_a, const void *_b) +{ + (void)opaque; + apfl_refcounted_string const *a = _a; + apfl_refcounted_string const *b = _b; + + return *a == *b || apfl_string_eq(*a, *b); +} + +static apfl_hash +scope_calc_hash(void *opaque, const void *_key) +{ + (void)opaque; + apfl_refcounted_string const *key = _key; + struct apfl_string_view sv = apfl_string_view_from(*key); + return apfl_hash_fnv1a(sv.bytes, sv.len); +} + +static void +scope_destroy_key(void *opaque, void *_key) +{ + (void)opaque; + apfl_refcounted_string *key = _key; + + apfl_refcounted_string_unref(*key); +} + +static void +scope_destroy_value(void *opaque, void *_value) +{ + (void)opaque; + variable *value = _value; + variable_unref(*value); +} + +static void +scope_copy_key(void *opaque, void *_dest, void *_src) +{ + (void)opaque; + apfl_refcounted_string *dest = _dest; + apfl_refcounted_string *src = _src; + *dest = apfl_refcounted_string_incref(*src); +} + +static void +scope_copy_value(void *opaque, void *_dest, void *_src) +{ + (void)opaque; + variable *dest = _dest; + variable *src = _src; + *dest = variable_incref(*src); +} + +static const struct apfl_hashmap_callbacks scope_hashmap_callbacks = { + .opaque = NULL, + .keys_eq = scope_keys_eq, + .calc_hash = scope_calc_hash, + .destroy_key = scope_destroy_key, + .destroy_value = scope_destroy_value, + .copy_key = scope_copy_key, + .copy_value = scope_copy_value, +}; + +apfl_hashmap +scope_new(void) +{ + return apfl_hashmap_new( + scope_hashmap_callbacks, + sizeof(apfl_refcounted_string), + sizeof(variable) + ); +} + +static variable +variable_new(void) +{ + variable var = ALLOC(struct variable_data); + if (var == NULL) { + return NULL; + } + + var->refcount = 1; + var->value.type = APFL_VALUE_NIL; + + return var; +} + +static variable +variable_incref(variable var) +{ + if (var != NULL) { + var->refcount++; + } + return var; +} + +static void +variable_unref(variable var) +{ + if (var != NULL && apfl_refcount_dec(&var->refcount)) { + apfl_value_deinit(&var->value); + free(var); + } +} apfl_ctx -apfl_ctx_new() +apfl_ctx_new(void) { apfl_ctx ctx = ALLOC(struct apfl_ctx_data); if (ctx == NULL) { return NULL; } + if ((ctx->scope = scope_new()) == NULL) { + free(ctx); + return NULL; + } + return ctx; } void apfl_ctx_destroy(apfl_ctx ctx) { + apfl_hashmap_destroy(ctx->scope); free(ctx); } +static variable +ctx_get_var_for_assignment_inner(apfl_ctx ctx, /*borrowed*/ apfl_refcounted_string name) +{ + variable var; + if (apfl_hashmap_get(ctx->scope, &name, &var)) { + return var; + } + + if ((var = variable_new()) == NULL) { + return NULL; + } + + if (!apfl_hashmap_set(ctx->scope, &name, &var)) { + variable_unref(var); + return NULL; + } + + return var; +} + +static variable +ctx_get_var_for_assignment(apfl_ctx ctx, apfl_refcounted_string name) +{ + variable var = ctx_get_var_for_assignment_inner(ctx, name); + apfl_refcounted_string_unref(name); + return var; +} + +static variable +ctx_get_var(apfl_ctx ctx, apfl_refcounted_string name) +{ + variable var; + bool ok = apfl_hashmap_get(ctx->scope, &name, &var); + apfl_refcounted_string_unref(name); + return ok ? var : NULL; +} + static struct apfl_result fatal(void) { @@ -269,6 +429,60 @@ evaluate_at(apfl_ctx ctx, struct apfl_expr_at *at) } } +static struct apfl_result +evaluate_assignment(apfl_ctx ctx, struct apfl_expr_assignment *assignment) +{ + // TODO: Use assignment->local flag. Sonce we don't hev functions yet, it's not yet relevant. + // TODO: Handle special variable _. Maybe the parser should already generate a different assignment type for it? + + if (assignment->lhs.type != APFL_EXPR_ASSIGNABLE_VAR) { + return (struct apfl_result) { .type = APFL_RESULT_ERR }; // TODO: Implement other assignable types + } + + apfl_refcounted_string varname = apfl_string_move_into_new_refcounted(&assignment->lhs.var); + if (varname == NULL) { + return fatal(); + } + + variable var = ctx_get_var_for_assignment(ctx, varname); + + if (var == NULL) { + return fatal(); + } + + struct apfl_result result = evaluate(ctx, assignment->rhs); + if (result.type != APFL_RESULT_OK) { + variable_unref(var); + return result; + } + + apfl_value_deinit(&var->value); + var->value = apfl_value_incref(result.value); + variable_unref(var); + return result; +} + +static struct apfl_result +evaluate_var(apfl_ctx ctx, struct apfl_string *varname) +{ + apfl_refcounted_string rcstring = apfl_string_move_into_new_refcounted(varname); + if (rcstring == NULL) { + return fatal(); + } + + variable var = ctx_get_var(ctx, rcstring); + if (var == NULL) { + return (struct apfl_result) { .type = APFL_RESULT_ERR }; + } + + struct apfl_value value = apfl_value_incref(var->value); + variable_unref(var); + return (struct apfl_result) { + .type = APFL_RESULT_OK, + .value = value, + }; +} + static struct apfl_result evaluate(apfl_ctx ctx, struct apfl_expr *expr) { @@ -283,11 +497,13 @@ evaluate(apfl_ctx ctx, struct apfl_expr *expr) return evaluate_dot(ctx, &expr->dot); case APFL_EXPR_AT: return evaluate_at(ctx, &expr->at); + case APFL_EXPR_ASSIGNMENT: + return evaluate_assignment(ctx, &expr->assignment); case APFL_EXPR_VAR: + return evaluate_var(ctx, &expr->var); case APFL_EXPR_CALL: case APFL_EXPR_SIMPLE_FUNC: case APFL_EXPR_COMPLEX_FUNC: - case APFL_EXPR_ASSIGNMENT: break; // Not implemented yet }