#include <math.h>
#include <string.h>
+#include "./builtins.h"
+#include "./expr.h"
#include "./interpreter.h"
+#include "./scope.h"
-struct Expr eval(struct Expr expr)
+struct EvalResult eval_success(struct Expr expr)
{
- /* TODO(#311): eval(expr) is not implemented */
- return expr;
+ struct EvalResult result = {
+ .is_error = false,
+ .expr = expr,
+ };
+
+ return result;
+}
+
+struct EvalResult eval_failure(struct Expr error)
+{
+ struct EvalResult result = {
+ .is_error = true,
+ .expr = error,
+ };
+
+ return result;
+}
+
+static struct EvalResult length(Gc *gc, struct Expr obj)
+{
+ if (!list_p(obj)) {
+ return eval_failure(list(gc, 3,
+ SYMBOL(gc, "wrong-argument-type"),
+ SYMBOL(gc, "listp"),
+ obj));
+ }
+
+ return eval_success(NUMBER(gc, length_of_list(obj)));
+}
+
+static struct EvalResult eval_atom(Gc *gc, struct Scope *scope, struct Atom *atom)
+{
+ (void) scope;
+ (void) gc;
+
+ switch (atom->type) {
+ case ATOM_NUMBER:
+ case ATOM_STRING:
+ return eval_success(atom_as_expr(atom));
+
+ case ATOM_SYMBOL: {
+ if (nil_p(atom_as_expr(atom))) {
+ return eval_success(atom_as_expr(atom));
+ }
+
+ struct Expr value = get_scope_value(scope, atom_as_expr(atom));
+
+ if (nil_p(value)) {
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "void-variable"),
+ atom_as_expr(atom)));
+ }
+
+ return eval_success(value.cons->cdr);
+ }
+ }
+
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "unexpected-expression"),
+ atom_as_expr(atom)));
+}
+
+static struct EvalResult eval_all_args(Gc *gc, struct Scope *scope, struct Expr args)
+{
+ (void) scope;
+ (void) args;
+
+ switch(args.type) {
+ case EXPR_ATOM:
+ return eval_atom(gc, scope, args.atom);
+
+ case EXPR_CONS: {
+ struct EvalResult car = eval(gc, scope, args.cons->car);
+ if (car.is_error) {
+ return car;
+ }
+
+ struct EvalResult cdr = eval_all_args(gc, scope, args.cons->cdr);
+ if (cdr.is_error) {
+ return cdr;
+ }
+
+ return eval_success(cons_as_expr(create_cons(gc, car.expr, cdr.expr)));
+ }
+
+ default: {}
+ }
+
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "unexpected-expression"),
+ args));
+}
+
+static struct EvalResult plus_op(Gc *gc, struct Expr args)
+{
+ long int result = 0.0f;
+
+ while (!nil_p(args)) {
+ if (args.type != EXPR_CONS) {
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "expected-cons"),
+ args));
+ }
+
+ if (args.cons->car.type != EXPR_ATOM ||
+ args.cons->car.atom->type != ATOM_NUMBER) {
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "expected-number"),
+ args.cons->car));
+ }
+
+ result += args.cons->car.atom->num;
+ args = args.cons->cdr;
+ }
+
+ return eval_success(atom_as_expr(create_number_atom(gc, result)));
+}
+
+static struct EvalResult call_callable(Gc *gc,
+ struct Scope *scope,
+ struct Expr callable,
+ struct Expr args) {
+ if (!callable_p(callable)) {
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "expected-callable"),
+ callable));
+ }
+
+ if (!list_p(args)) {
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "expected-list"),
+ args));
+ }
+
+ struct Expr vars = callable.cons->cdr.cons->car;
+
+ if (length_of_list(args) != length_of_list(vars)) {
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "wrong-number-of-arguments"),
+ NUMBER(gc, length_of_list(args))));
+ }
+
+ push_scope_frame(gc, scope, vars, args);
+ struct Expr body = callable.cons->cdr.cons->cdr;
+
+ struct EvalResult result = eval_success(NIL(gc));
+
+ while (!nil_p(body)) {
+ print_expr_as_sexpr(body.cons->car);
+ result = eval(gc, scope, body.cons->car);
+ if (result.is_error) {
+ return result;
+ }
+ body = body.cons->cdr;
+ }
+
+ pop_scope_frame(gc, scope);
+
+ return result;
+}
+
+static struct EvalResult eval_funcall(Gc *gc, struct Scope *scope, struct Cons *cons)
+{
+ assert(cons);
+ (void) scope;
+
+ if (symbol_p(cons->car)) {
+ if (strcmp(cons->car.atom->sym, "+") == 0) {
+ struct EvalResult args = eval_all_args(gc, scope, cons->cdr);
+ if (args.is_error) {
+ return args;
+ }
+ return plus_op(gc, args.expr);
+ } else if (strcmp(cons->car.atom->sym, "set") == 0) {
+ struct Expr args = cons->cdr;
+ struct EvalResult n = length(gc, args);
+
+ if (n.is_error) {
+ return n;
+ }
+
+ if (n.expr.atom->num != 2) {
+ return eval_failure(list(gc, 3,
+ SYMBOL(gc, "wrong-number-of-arguments"),
+ SYMBOL(gc, "set"),
+ NUMBER(gc, n.expr.atom->num)));
+ }
+
+ struct Expr name = args.cons->car;
+ if (!symbol_p(name)) {
+ return eval_failure(list(gc, 3,
+ SYMBOL(gc, "wrong-type-argument"),
+ SYMBOL(gc, "symbolp"),
+ name));
+ }
+
+ struct EvalResult value = eval(gc, scope, args.cons->cdr.cons->car);
+ if (value.is_error) {
+ return value;
+ }
+
+ set_scope_value(gc, scope, name, value.expr);
+
+ return eval_success(value.expr);
+ } else if (strcmp(cons->car.atom->sym, "quote") == 0) {
+ /* TODO: quote does not check the amout of it's arguments */
+ return eval_success(cons->cdr.cons->car);
+ } else if (strcmp(cons->car.atom->sym, "lambda") == 0) {
+ return eval_success(cons_as_expr(cons));
+ } else {
+ struct EvalResult r = eval_all_args(gc, scope, cons_as_expr(cons));
+
+ if (r.is_error) {
+ return r;
+ }
+
+ if (!callable_p(r.expr.cons->car)) {
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "not-callable"),
+ r.expr.cons->car));
+ }
+
+ return call_callable(gc, scope, r.expr.cons->car, r.expr.cons->cdr);
+ }
+ } else if (callable_p(cons->car)) {
+ /* TODO: Call cons->car */
+ }
+
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "unknown-function"),
+ cons->car));
+}
+
+struct EvalResult eval(Gc *gc, struct Scope *scope, struct Expr expr)
+{
+ switch(expr.type) {
+ case EXPR_ATOM:
+ return eval_atom(gc, scope, expr.atom);
+
+ case EXPR_CONS:
+ return eval_funcall(gc, scope, expr.cons);
+
+ default: {}
+ }
+
+ return eval_failure(CONS(gc,
+ SYMBOL(gc, "unexpected-expression"),
+ expr));
}