Implement simple variable support
We can now assign to variables and retrieve their values. Not all assignables are implemented yet, but still pretty cool :).
This commit is contained in:
parent
bab1812cc9
commit
e928f40da4
1 changed files with 219 additions and 3 deletions
222
src/eval.c
222
src/eval.c
|
|
@ -1,32 +1,192 @@
|
|||
#include <assert.h>
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue