aboutsummaryrefslogtreecommitdiff
path: root/src/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval.c')
-rw-r--r--src/eval.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/eval.c b/src/eval.c
new file mode 100644
index 0000000..c1a6a10
--- /dev/null
+++ b/src/eval.c
@@ -0,0 +1,333 @@
+#include "eval.h"
+#include "ast.h"
+#include "object.h"
+#include "token.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+static struct object obj_null = {
+ OBJECT_NULL,
+ 2,
+ { 0 },
+};
+static struct object obj_true = {
+ OBJECT_BOOL,
+ 2,
+ .boolean = true,
+};
+static struct object obj_false = {
+ OBJECT_BOOL,
+ 2,
+ .boolean = false,
+};
+
+static inline struct object *
+get_bool_object(bool val)
+{
+ struct object *obj = val ? &obj_true : &obj_false;
+ return obj;
+}
+
+static struct object *
+new_error(char *fmt, ...)
+{
+ char *msg = malloc(1024);
+ struct object *obj = object_new(msg);
+
+ va_list args;
+ va_start(args, fmt);
+ vsprintf(obj->error.msg, fmt, args);
+ va_end(args);
+
+ return obj;
+}
+
+static struct object *
+eval_statements(struct environment *env, struct vector *sts)
+{
+ size_t i;
+ struct object *res;
+ struct statement *st;
+ vector_foreach(sts, i, st) {
+ res = eval(env, st);
+ if (res->type == OBJECT_RETURN || res->type == OBJECT_ERROR) {
+ return res;
+ }
+ }
+ return res;
+}
+
+static inline struct object *
+eval_bang_prefix_expression(struct environment *env, struct object *right)
+{
+ struct object *res;
+ res = get_bool_object(!right->integer);
+ object_unref(right);
+ return res;
+}
+
+static inline struct object *
+eval_minus_prefix_expression(struct environment *env, struct object *right)
+{
+ struct object *res;
+ if (right->type != OBJECT_INT) {
+ struct object *error = new_error("unknown operator: -%s",
+ object_type_print(right->type));
+ object_unref(right);
+ return error;
+ }
+ res = object_new(-right->integer);
+ object_unref(right);
+ return res;
+}
+
+static struct object *
+eval_prefix_expression(struct environment *env, struct token op,
+ struct object *right)
+{
+ struct object *res;
+ switch (op.type) {
+ case TOKEN_BANG:
+ return eval_bang_prefix_expression(env, right);
+ case TOKEN_MINUS:
+ return eval_minus_prefix_expression(env, right);
+ default:;
+ char buf[256];
+ struct object *error = new_error("unknown operator: %s%s",
+ slice_sprint(&op.literal, buf), object_type_print(right->type));
+ object_unref(right);
+ return error;
+ }
+}
+
+static inline struct object *
+eval_integer_infix_expression(struct environment *env, struct token op,
+ struct object *left, struct object *right)
+{
+ bool bres;
+ struct object *ires;
+ switch (op.type) {
+ case TOKEN_PLUS:
+ ires = object_new(left->integer + right->integer);
+ break;
+ case TOKEN_MINUS:
+ ires = object_new(left->integer - right->integer);
+ break;
+ case TOKEN_ASTERISK:
+ ires = object_new(left->integer * right->integer);
+ break;
+ case TOKEN_SLASH:
+ ires = object_new(left->integer / right->integer);
+ break;
+ case TOKEN_LT:
+ bres = left->integer < right->integer;
+ goto boolres;
+ case TOKEN_GT:
+ bres = left->integer > right->integer;
+ goto boolres;
+ case TOKEN_EQ:
+ bres = left->integer == right->integer;
+ goto boolres;
+ case TOKEN_NOTEQ:
+ bres = left->integer != right->integer;
+ goto boolres;
+ default:;
+ char buf[256];
+ struct object *error = new_error("unknown operator: %s %s %s",
+ object_type_print(left->type), slice_sprint(&op.literal, buf),
+ object_type_print(right->type));
+ object_unref(left);
+ object_unref(right);
+ return error;
+ }
+ object_unref(left);
+ object_unref(right);
+ return ires;
+boolres:
+ object_unref(left);
+ object_unref(right);
+ return get_bool_object(bres);
+}
+
+static inline struct object *
+eval_infix_expression(struct environment *env, struct token op,
+ struct object *left, struct object *right)
+{
+ if (left->type == OBJECT_INT && right->type == OBJECT_INT) {
+ return eval_integer_infix_expression(env, op, left, right);
+ }
+ bool res;
+ switch (op.type) {
+ case TOKEN_EQ:
+ res = left->integer == right->integer;
+ break;
+ case TOKEN_NOTEQ:
+ res = left->integer != right->integer;
+ break;
+ default:;
+ char buf[256];
+ struct object *error;
+ if (left->type != right->type) {
+ error = new_error("type mismatch: %s %s %s",
+ object_type_print(left->type), slice_sprint(&op.literal, buf),
+ object_type_print(right->type));
+ } else {
+ error = new_error("unknown operator: %s %s %s",
+ object_type_print(left->type), slice_sprint(&op.literal, buf),
+ object_type_print(right->type));
+ }
+ object_unref(left);
+ object_unref(right);
+ return error;
+ }
+ object_unref(left);
+ object_unref(right);
+ return get_bool_object(res);
+}
+
+static inline struct object *
+eval_if_expression(struct environment *env, struct if_expression *expr)
+{
+ struct object *cond = eval(env, expr->condition);
+ struct object *res;
+ if (cond->boolean) {
+ res = eval(env, expr->consequence);
+ } else if (expr->alternative) {
+ res = eval(env, expr->alternative);
+ } else {
+ res = &obj_null;
+ }
+ object_unref(cond);
+ return res;
+}
+
+static inline struct object *
+eval_call_expression(struct environment *env, struct expression *expr)
+{
+ struct object *func = eval(env, expr->call.func);
+ if (func->type == OBJECT_ERROR) {
+ return func;
+ } else if (func->type != OBJECT_FUNC) {
+ char buf[256];
+ struct object *err = new_error("not a function: %s",
+ object_sprint(func, buf));
+ object_unref(func);
+ return err;
+ }
+ size_t i;
+ struct expression *arg;
+ struct environment *fenv = environment_new_enclosed(env);
+ vector_foreach(expr->call.arguments, i, arg) {
+ struct object *obj = eval(env, arg);
+ if (obj->type == OBJECT_ERROR) {
+ object_unref(func);
+ return obj;
+ }
+ struct expression *param = func->func.params->values[i];
+ environment_set(fenv, param->ident.value, obj);
+ object_unref(obj);
+ }
+ struct object *res = eval(fenv, func->func.body);
+ if (res->type == OBJECT_RETURN) {
+ struct object *ret = res;
+ res = ret->retrn.value;
+ object_ref(res);
+ object_unref(ret);
+ }
+ object_unref(func);
+ environment_destroy(fenv);
+ return res;
+}
+
+struct object *
+eval_expression(struct environment *env, struct expression *expr)
+{
+ struct object *res = NULL;
+ switch (expr->type) {
+ case EXPRESSION_INT:
+ res = object_new(expr->integer.value);
+ return res;
+ case EXPRESSION_BOOL:
+ return expr->boolean.value ? &obj_true : &obj_false;
+ case EXPRESSION_PREFIX:
+ res = eval(env, expr->prefix.right);
+ if (res == OBJECT_ERROR) return res;
+ return eval_prefix_expression(env, expr->token, res);
+ case EXPRESSION_INFIX:;
+ struct object *left = eval(env, expr->infix.left);
+ if (left == OBJECT_ERROR) return res;
+ struct object *right = eval(env, expr->infix.right);
+ if (right == OBJECT_ERROR) {
+ object_unref(left);
+ return right;
+ }
+ return eval_infix_expression(env, expr->token, left, right);
+ case EXPRESSION_IF:
+ return eval_if_expression(env, &expr->cond);
+ case EXPRESSION_IDENT:
+ res = environment_get(env, &expr->ident.value);
+ if (!res) {
+ char ibuf[256];
+ return new_error("not declared: %s",
+ slice_sprint(&expr->ident.value, ibuf));
+ }
+ object_ref(res);
+ return res;
+ case EXPRESSION_FUNC:
+ res = object_new(expr);
+ return res;
+ case EXPRESSION_CALL:
+ return eval_call_expression(env, expr);
+ default:
+ return NULL;
+ }
+}
+
+struct object *
+eval_statement(struct environment *env, struct statement *st)
+{
+ struct object *val;
+ switch (st->type) {
+ case STATEMENT_EXPRESSION:
+ return eval(env, st->expr.expr);
+ case STATEMENT_BLOCK:
+ return eval_statements(env, st->block.statements);
+ case STATEMENT_RETURN:
+ val = eval(env, st->retrn.value);
+ if (val->type == OBJECT_ERROR) return val;
+ struct object *res = object_new(val);
+ return res;
+ case STATEMENT_LET:
+ val = eval(env, st->let.value);
+ if (val->type == OBJECT_ERROR) return val;
+ struct object *old = environment_set(env, st->let.name->value, val);
+ if (old) object_unref(old);
+ object_unref(val);
+ default:
+ return NULL;
+ }
+}
+
+struct object *
+eval_program(struct environment *env, struct program *prog)
+{
+ size_t i;
+ struct object *res = NULL;
+ struct statement *st;
+ vector_foreach(prog->statements, i, st) {
+ if (res) object_unref(res);
+ res = eval(env, st);
+ if (res) {
+ if (res->type == OBJECT_RETURN) {
+ struct object *val = res->retrn.value;
+ object_ref(val);
+ object_unref(res);
+ return val;
+ } else if (res->type == OBJECT_ERROR) {
+ return res;
+ }
+ }
+ }
+ return res;
+}