]> git.lizzy.rs Git - nothing.git/blobdiff - src/script/interpreter.c
Support lambdas
[nothing.git] / src / script / interpreter.c
index e598ac77852f61e201be268961ca004b5aba11e5..07b716381aa14b5e43475a3ae30c2ca32a30e075 100644 (file)
 #include "./builtins.h"
 #include "./expr.h"
 #include "./interpreter.h"
+#include "./scope.h"
 
 struct EvalResult eval_success(struct Expr expr)
 {
     struct EvalResult result = {
         .is_error = false,
         .expr = expr,
-        .error = NULL
     };
 
     return result;
 }
 
-struct EvalResult eval_failure(const char *error, struct Expr expr)
+struct EvalResult eval_failure(struct Expr error)
 {
     struct EvalResult result = {
         .is_error = true,
-        .error = error,
-        .expr = expr
+        .expr = error,
     };
 
     return result;
 }
 
-static struct EvalResult eval_atom(struct Expr scope, struct Atom *atom)
+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:
-        /* TODO: Evaluating symbols is not implemented */
-        return eval_failure("Evaluating symbols is not implemented",
-                            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("Unexpected expression", atom_as_expr(atom));
+    return eval_failure(CONS(gc,
+                             SYMBOL(gc, "unexpected-expression"),
+                             atom_as_expr(atom)));
 }
 
-static struct EvalResult eval_args(struct Expr scope, struct Expr args)
+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(scope, args.atom);
+        return eval_atom(gc, scope, args.atom);
 
     case EXPR_CONS: {
-        struct EvalResult car = eval(scope, args.cons->car);
+        struct EvalResult car = eval(gc, scope, args.cons->car);
         if (car.is_error) {
             return car;
         }
 
-        struct EvalResult cdr = eval_args(scope, args.cons->cdr);
+        struct EvalResult cdr = eval_all_args(gc, scope, args.cons->cdr);
         if (cdr.is_error) {
             return cdr;
         }
 
-        /* TODO: memory leak */
-        return eval_success(cons_as_expr(create_cons(car.expr, cdr.expr)));
+        return eval_success(cons_as_expr(create_cons(gc, car.expr, cdr.expr)));
     }
 
     default: {}
     }
 
-    return eval_failure("Unexpected expression", args);
+    return eval_failure(CONS(gc,
+                             SYMBOL(gc, "unexpected-expression"),
+                             args));
 }
 
-static struct EvalResult plus_op(struct Expr args)
+static struct EvalResult plus_op(Gc *gc, struct Expr args)
 {
-    float result = 0.0f;
+    long int result = 0.0f;
 
     while (!nil_p(args)) {
         if (args.type != EXPR_CONS) {
-            return eval_failure("Expected cons", args);
+            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("Expected number", args.cons->car);
+            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(result)));
+    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(struct Expr scope, struct Cons *cons)
+static struct EvalResult eval_funcall(Gc *gc, struct Scope *scope, struct Cons *cons)
 {
     assert(cons);
     (void) scope;
 
-    if (cons->car.type != EXPR_ATOM && cons->car.atom->type != ATOM_SYMBOL) {
-        return eval_failure("Not a function", cons->car);
-    }
+    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));
+            }
 
-    if (strcmp(cons->car.atom->sym, "+")) {
-        struct EvalResult args = eval_args(scope, cons->cdr);
-        if (args.is_error) {
-            return args;
+            return call_callable(gc, scope, r.expr.cons->car, r.expr.cons->cdr);
         }
-        return plus_op(args.expr);
+    } else if (callable_p(cons->car)) {
+        /* TODO: Call cons->car */
     }
 
-    return eval_failure("Unknown function", cons->car);
+    return eval_failure(CONS(gc,
+                             SYMBOL(gc, "unknown-function"),
+                             cons->car));
 }
 
-struct EvalResult eval(struct Expr scope, struct Expr expr)
+struct EvalResult eval(Gc *gc, struct Scope *scope, struct Expr expr)
 {
     switch(expr.type) {
     case EXPR_ATOM:
-        return eval_atom(scope, expr.atom);
+        return eval_atom(gc, scope, expr.atom);
 
     case EXPR_CONS:
-        return eval_funcall(scope, expr.cons);
+        return eval_funcall(gc, scope, expr.cons);
 
     default: {}
     }
 
-    return eval_failure("Unexpected expression", expr);
+    return eval_failure(CONS(gc,
+                             SYMBOL(gc, "unexpected-expression"),
+                             expr));
 }