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:
parent
80ad5c979d
commit
649607ce50
6 changed files with 403 additions and 0 deletions
|
|
@ -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
|
||||
|
|
|
|||
77
src/apfl.h
77
src/apfl.h
|
|
@ -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
188
src/eval.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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
133
src/value.c
Normal 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;
|
||||
}
|
||||
Loading…
Reference in a new issue