diff options
| author | Felix Perktold <Felix.Perktold@student.uibk.ac.at> | 2026-01-21 17:12:58 +0100 |
|---|---|---|
| committer | Felix Perktold <Felix.Perktold@student.uibk.ac.at> | 2026-01-21 17:12:58 +0100 |
| commit | 77df6486f9ed77e38a554cdaa6e28bea348f4788 (patch) | |
| tree | 44cb40d830c6901c1e2aa88825a4603281875efa | |
| parent | a5484c8bf3ac4c85f40ba0be5df036ce171cdc04 (diff) | |
add primitive lazy-eval (STILL BROKEN!)
| -rw-r--r-- | lisp.c | 60 | ||||
| -rw-r--r-- | lisp_api.h | 10 | ||||
| -rw-r--r-- | modules/std_lib.c | 7 |
3 files changed, 67 insertions, 10 deletions
@@ -69,6 +69,43 @@ value *make_error(const char *s) { return val; } +value *make_thunk(env *e, value *expr) { + // don't thunk self-evaluating expressions + if (expr->type == VT_INT || + expr->type == VT_DOUBLE || + expr->type == VT_STRING || + expr->type == VT_NIL) { + return expr; + } + + value *val = malloc(sizeof(value)); + val->type = VT_THUNK; + val->as.thunk.expr = expr; + val->as.thunk.cached = NULL; + val->as.thunk.env = e; + return val; +} + +value *force_thunk(value *v) { + if(v->type != VT_THUNK) { + return v; + } + if(v->as.thunk.cached) { + printf("\nDEBUG: thunk "); + print_value(v->as.thunk.expr); + printf(" was already cached as: "); + println_value(v->as.thunk.cached); + return v->as.thunk.cached; + } + printf("\nDEBUG: thunk "); + print_value(v->as.thunk.expr); + printf(" is now cached as: "); + println_value(v->as.thunk.cached); + + v->as.thunk.cached = eval(v->as.thunk.env, v->as.thunk.expr); + return v; +} + value *cons(value *car, value *cdr) { value *val = malloc(sizeof(value)); val->type = VT_PAIR; @@ -170,9 +207,13 @@ void print_value(value *val) { case VT_ERROR: printf("(error \"%s\")", val->as.err); break; + + case VT_THUNK: + print_value(force_thunk(val)); + break; default: - fprintf(stderr, "<unknown>[as str:%s][as f:%f][as int:%d]\n", val->as.str, val->as.d, val->as.i); - fprintf(stderr, "<unknown>[as pair: (%s . %s)\n", val->as.pair.car, val->as.pair.cdr); + printf("\n<unknown>[as str:%s][as f:%f][as int:%d]\n", val->as.str, val->as.d, val->as.i); + printf("<unknown>[as pair: (%s . %s)\n", val->as.pair.car, val->as.pair.cdr); break; } } @@ -236,7 +277,7 @@ value *eval(env *e, value *v) { return make_error("symbol not found"); ///return list(make_symbol("quote"), v); //TODO: return error } - return found; + return force_thunk(found); } //pairs get special treatment @@ -254,6 +295,7 @@ value *eval_pair(env *e, value *v) { value *head = car(v); value *tail = cdr(v); + // special forms if (head->type == VT_SYMBOL && !strcmp(head->as.sym, "quote")) { return car(tail); } @@ -282,7 +324,7 @@ value *eval_pair(env *e, value *v) { } if (head->type == VT_SYMBOL && !strcmp(head->as.sym, "if")) { - value *test = eval(e, car(tail)); + value *test = force_thunk(eval(e, car(tail))); if(test->type != VT_NIL) { return eval(e, car(cdr(tail))); } else { @@ -296,18 +338,18 @@ value *eval_pair(env *e, value *v) { return v; } - value *head_eval = eval(e, head); + value *head_eval = force_thunk(eval(e, head)); if (head_eval->type == VT_PROCEDURE) { return head_eval->as.procedure.fn(e, tail); } if (head_eval->type == VT_LAMBDA) { - //evaluate arguments first, then apply - value *args_eval = make_nil(); + //wrap arguments as thunks + value *args_thunks = make_nil(); for(value *a = tail; a->type == VT_PAIR; a = cdr(a)) { - args_eval = cons(eval(e, car(a)), args_eval); + args_thunks = cons(make_thunk(e, car(a)), args_thunks); } - return apply(head_eval, reverse(args_eval)); + return apply(head_eval, reverse(args_thunks)); } // else evaluate the list recursively @@ -18,7 +18,8 @@ typedef enum { VT_PAIR, VT_LAMBDA, VT_PROCEDURE, - VT_ERROR + VT_ERROR, + VT_THUNK } val_type; struct value { @@ -41,6 +42,11 @@ struct value { struct { value *(*fn) (env *, value *); } procedure; + struct { + value *expr; + value *cached; + env *env; + } thunk; } as; }; @@ -52,6 +58,8 @@ value *make_nil(); value *make_lambda(env *e, value *params, value *body); value *make_procedure(value *(*fn) (env *, value *)); value *make_error(const char *str); +value *make_thunk(env *e, value *expr); +value *force_thunk(value *v); value *cons(value *car, value *cdr); value *car(value *cons); value *cdr(value *cons); diff --git a/modules/std_lib.c b/modules/std_lib.c index f87f2db..a38c5ff 100644 --- a/modules/std_lib.c +++ b/modules/std_lib.c @@ -22,6 +22,7 @@ value *procedure_reverse(env *e, value *args) { value *procedure_isnull(env *e, value *args) { value *v = eval(e, car(args)); + v = force_thunk(v); if(v->type == VT_NIL) { return make_int(1); } @@ -29,6 +30,8 @@ value *procedure_isnull(env *e, value *args) { } int value_eq(value *a, value *b) { + a = force_thunk(a); + b = force_thunk(b); if(a->type != b->type){ return 0; } @@ -54,10 +57,12 @@ int value_eq(value *a, value *b) { value *procedure_equal(env *e, value *args) { value *v_prev = eval(e, car(args)); + v_prev = force_thunk(v_prev); args = cdr(args); while (args->type == VT_PAIR) { value *v_cur = eval(e, car(args)); + v_cur = force_thunk(v_cur); if (!value_eq(v_prev, v_cur)) { return make_nil(); } @@ -71,6 +76,7 @@ value *procedure_equal(env *e, value *args) { value *apply_to_nums(env *e, value *args, double (*fn)(double, double)) { double result; value *v = eval(e, car(args)); + v = force_thunk(v); if (v->type == VT_INT) { result = v->as.i; } else if (v->type == VT_DOUBLE) { @@ -125,6 +131,7 @@ value *procedure_div(env *e, value *args) { value *procedure_lt(env *e, value *args) { double prev_num; value *v = eval(e, car(args)); + v = force_thunk(v); if (v->type == VT_INT) { prev_num = v->as.i; |
