Start work on evaluating expressions

Right now we're only evaluating bool/nil/number/string/list literals, but
it's a start :).
This commit is contained in:
Laria 2022-01-02 17:19:54 +01:00
parent 80ad5c979d
commit 649607ce50
6 changed files with 403 additions and 0 deletions

View file

@ -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

View file

@ -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

188
src/eval.c Normal file
View file

@ -0,0 +1,188 @@
#include <assert.h>
#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;
}

View file

@ -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

133
src/value.c Normal file
View file

@ -0,0 +1,133 @@
#include <assert.h>
#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;
}

View file