diff options
author | Bad Diode <bd@badd10de.dev> | 2021-10-14 17:05:37 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-10-14 17:05:37 +0200 |
commit | 530ed15ec941194e661010498cf30b7842710939 (patch) | |
tree | ccd1d771350453b59049e06ac224728f321f2b15 | |
parent | ab23395b1fc88bbc63bef88de3477cc316857ace (diff) | |
download | bdl-530ed15ec941194e661010498cf30b7842710939.tar.gz bdl-530ed15ec941194e661010498cf30b7842710939.zip |
Fix lambda and closures
-rw-r--r-- | examples/variables.bdl | 56 | ||||
-rw-r--r-- | src/bootstrap/environment.c | 52 | ||||
-rw-r--r-- | src/bootstrap/primitives.c | 55 |
3 files changed, 131 insertions, 32 deletions
diff --git a/examples/variables.bdl b/examples/variables.bdl index 6097368..2e2543a 100644 --- a/examples/variables.bdl +++ b/examples/variables.bdl | |||
@@ -14,3 +14,59 @@ | |||
14 | (print "(error? (set! a 42)) -> ") (error? (set! a 42)) | 14 | (print "(error? (set! a 42)) -> ") (error? (set! a 42)) |
15 | (print "a -> ") a | 15 | (print "a -> ") a |
16 | (print "(error? (set! b 99)) -> ") (error? (set! b 99)) | 16 | (print "(error? (set! b 99)) -> ") (error? (set! b 99)) |
17 | (lambda (a b) (+ 10 a b)) | ||
18 | |||
19 | (def a 20) | ||
20 | ((lambda (a b) (+ 10 a b)) 1 2) | ||
21 | ((lambda (a b) (+ 10 a b)) a 3) | ||
22 | (def myfun (lambda (a b) (+ a b))) | ||
23 | (myfun 6 9) | ||
24 | (+ 1 (myfun 10 (myfun a a)) 30) | ||
25 | (myfun 10 (myfun 5 0)) | ||
26 | |||
27 | ;; Closures. | ||
28 | (def make-counter (lambda () | ||
29 | (def value 0) | ||
30 | (def counter (lambda () | ||
31 | (set! value (+ value 1)) | ||
32 | value)) | ||
33 | counter)) | ||
34 | |||
35 | (def counter-a (make-counter)) | ||
36 | (def counter-b (make-counter)) | ||
37 | (counter-a) | ||
38 | (counter-a) | ||
39 | (counter-b) | ||
40 | (counter-b) | ||
41 | (counter-b) | ||
42 | (counter-b) | ||
43 | (counter-a) | ||
44 | |||
45 | ;; Fibonacci. | ||
46 | (def fib (lambda (n) | ||
47 | (if (<= n 2) | ||
48 | 1 | ||
49 | (+ (fib (- n 1)) (fib (- n 2)))))) | ||
50 | (fib 15) | ||
51 | |||
52 | ;; Lambda capture. | ||
53 | (def a 20) | ||
54 | (def b (lambda () | ||
55 | (display a) | ||
56 | (newline) | ||
57 | (def a 42) | ||
58 | (display a) | ||
59 | (newline))) | ||
60 | |||
61 | (b) | ||
62 | (b) | ||
63 | |||
64 | |||
65 | ;; Infinite loop. (For teseting purposes) | ||
66 | ; (def test (lambda (n) | ||
67 | ; (print "ITER\n") | ||
68 | ; (if (<= n 2) | ||
69 | ; 'ok | ||
70 | ; (test (+ n 1))))) | ||
71 | |||
72 | ; (test 3) | ||
diff --git a/src/bootstrap/environment.c b/src/bootstrap/environment.c index 99dd7fd..e111753 100644 --- a/src/bootstrap/environment.c +++ b/src/bootstrap/environment.c | |||
@@ -60,8 +60,27 @@ env_lookup(Environment *env, Object *symbol) { | |||
60 | return obj_err; | 60 | return obj_err; |
61 | } | 61 | } |
62 | 62 | ||
63 | Object * | ||
64 | env_update(Environment *env, Object *symbol, Object *value) { | ||
65 | while (env != NULL) { | ||
66 | for (size_t i = 0; i < env->size; i++) { | ||
67 | EnvEntry entry = env->buf[i]; | ||
68 | if (obj_eq(symbol, entry.symbol)) { | ||
69 | env->buf[i].value = obj_duplicate(value); | ||
70 | return obj_nil; | ||
71 | } | ||
72 | } | ||
73 | env = env->parent; | ||
74 | } | ||
75 | error_push((Error){ | ||
76 | .type = ERR_TYPE_RUNTIME, | ||
77 | .value = ERR_SYMBOL_NOT_FOUND, | ||
78 | }); | ||
79 | return obj_err; | ||
80 | } | ||
81 | |||
63 | ssize_t | 82 | ssize_t |
64 | env_symbol_index_in_current_env(Environment *env, Object *symbol) { | 83 | env_index_current(Environment *env, Object *symbol) { |
65 | for (size_t i = 0; i < env->size; i++) { | 84 | for (size_t i = 0; i < env->size; i++) { |
66 | EnvEntry entry = env->buf[i]; | 85 | EnvEntry entry = env->buf[i]; |
67 | if (obj_eq(symbol, entry.symbol)) { | 86 | if (obj_eq(symbol, entry.symbol)) { |
@@ -70,3 +89,34 @@ env_symbol_index_in_current_env(Environment *env, Object *symbol) { | |||
70 | } | 89 | } |
71 | return -1; | 90 | return -1; |
72 | } | 91 | } |
92 | |||
93 | void | ||
94 | env_add_or_update_current(Environment *env, Object *symbol, Object *value) { | ||
95 | ssize_t index = env_index_current(env, symbol); | ||
96 | if (index == -1) { | ||
97 | env_add_symbol(env, obj_duplicate(symbol), obj_duplicate(value)); | ||
98 | } else { | ||
99 | env->buf[index].value = obj_duplicate(value); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | Environment * | ||
104 | env_extend(Environment *parent, Environment *extra) { | ||
105 | Environment *env = env_create(parent); | ||
106 | for (size_t i = 0; i < extra->size; i++) { | ||
107 | EnvEntry entry = extra->buf[i]; | ||
108 | Environment *tmp = env; | ||
109 | ssize_t idx = -1; | ||
110 | while (tmp != NULL) { | ||
111 | idx = env_index_current(tmp, entry.symbol); | ||
112 | if (idx != -1) { | ||
113 | break; | ||
114 | } | ||
115 | tmp = tmp->parent; | ||
116 | } | ||
117 | if (idx == -1) { | ||
118 | env_add_symbol(env, obj_duplicate(entry.symbol), obj_duplicate(entry.value)); | ||
119 | } | ||
120 | } | ||
121 | return env; | ||
122 | } | ||
diff --git a/src/bootstrap/primitives.c b/src/bootstrap/primitives.c index 461799e..4ef56d5 100644 --- a/src/bootstrap/primitives.c +++ b/src/bootstrap/primitives.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #define DEBUG_OBJ(MSG,OBJ) printf((MSG)); display(OBJ); printf("\n"); | ||
2 | |||
1 | Object * | 3 | Object * |
2 | eval(Environment* env, Object *root) { | 4 | eval(Environment* env, Object *root) { |
3 | tailcall: | ||
4 | switch (root->type) { | 5 | switch (root->type) { |
5 | case OBJ_TYPE_ERR: | 6 | case OBJ_TYPE_ERR: |
6 | case OBJ_TYPE_PROCEDURE: | 7 | case OBJ_TYPE_PROCEDURE: |
@@ -35,17 +36,26 @@ tailcall: | |||
35 | if (val->type == OBJ_TYPE_PROCEDURE) { | 36 | if (val->type == OBJ_TYPE_PROCEDURE) { |
36 | return val->proc(env, root->cdr); | 37 | return val->proc(env, root->cdr); |
37 | } | 38 | } |
39 | if (val->type == OBJ_TYPE_LAMBDA) { | ||
40 | goto eval_lambda; | ||
41 | } | ||
38 | error_push((Error){ | 42 | error_push((Error){ |
39 | .type = ERR_TYPE_RUNTIME, | 43 | .type = ERR_TYPE_RUNTIME, |
40 | .value = ERR_OBJ_NOT_CALLABLE, | 44 | .value = ERR_OBJ_NOT_CALLABLE, |
41 | }); | 45 | }); |
42 | return obj_err; | 46 | return obj_err; |
43 | } | 47 | } |
44 | Object* lambda = eval(env, root->car); | 48 | Object* lambda; |
49 | eval_lambda: | ||
50 | lambda = eval(env, root->car); | ||
51 | if (lambda == obj_err) { | ||
52 | return obj_err; | ||
53 | } | ||
45 | if (lambda->type == OBJ_TYPE_LAMBDA) { | 54 | if (lambda->type == OBJ_TYPE_LAMBDA) { |
46 | Object *fun = lambda; | 55 | Object *fun = lambda; |
47 | Object *args = root->cdr; | 56 | Object *args = root->cdr; |
48 | Object *params = fun->args; | 57 | Object *params = fun->args; |
58 | env = env_extend(fun->env, env); | ||
49 | while (params != obj_nil) { | 59 | while (params != obj_nil) { |
50 | if (args == obj_nil) { | 60 | if (args == obj_nil) { |
51 | error_push((Error){ | 61 | error_push((Error){ |
@@ -66,12 +76,7 @@ tailcall: | |||
66 | }); | 76 | }); |
67 | return obj_err; | 77 | return obj_err; |
68 | } | 78 | } |
69 | ssize_t index = env_symbol_index_in_current_env(fun->env, symbol); | 79 | env_add_or_update_current(env, symbol, value); |
70 | if (index == -1) { | ||
71 | env_add_symbol(fun->env, obj_duplicate(symbol), obj_duplicate(value)); | ||
72 | } else { | ||
73 | fun->env->buf[index].value = obj_duplicate(value); | ||
74 | } | ||
75 | args = args->cdr; | 80 | args = args->cdr; |
76 | params = params->cdr; | 81 | params = params->cdr; |
77 | } | 82 | } |
@@ -82,10 +87,14 @@ tailcall: | |||
82 | }); | 87 | }); |
83 | return obj_err; | 88 | return obj_err; |
84 | } | 89 | } |
85 | |||
86 | env = fun->env; | ||
87 | root = fun->body; | 90 | root = fun->body; |
88 | goto tailcall; | 91 | while (root->cdr != obj_nil) { |
92 | if (eval(env, root->car) == obj_err) { | ||
93 | return obj_err; | ||
94 | }; | ||
95 | root = root->cdr; | ||
96 | } | ||
97 | return eval(env, root->car); | ||
89 | } | 98 | } |
90 | } break; | 99 | } break; |
91 | } | 100 | } |
@@ -693,13 +702,7 @@ proc_define(Environment *env, Object *obj) { | |||
693 | return obj_err; | 702 | return obj_err; |
694 | } | 703 | } |
695 | 704 | ||
696 | // Make a copy of the symbol and to make them permanent in the environment. | 705 | env_add_or_update_current(env, symbol, value); |
697 | ssize_t index = env_symbol_index_in_current_env(env, symbol); | ||
698 | if (index == -1) { | ||
699 | env_add_symbol(env, obj_duplicate(symbol), obj_duplicate(value)); | ||
700 | } else { | ||
701 | env->buf[index].value = obj_duplicate(value); | ||
702 | } | ||
703 | return obj_nil; | 706 | return obj_nil; |
704 | } | 707 | } |
705 | 708 | ||
@@ -727,17 +730,7 @@ proc_set(Environment *env, Object *obj) { | |||
727 | return obj_err; | 730 | return obj_err; |
728 | } | 731 | } |
729 | 732 | ||
730 | ssize_t index = env_symbol_index_in_current_env(env, symbol); | 733 | return env_update(env, symbol, value); |
731 | if (index == -1) { | ||
732 | error_push((Error){ | ||
733 | .type = ERR_TYPE_RUNTIME, | ||
734 | .value = ERR_SYMBOL_NOT_FOUND, | ||
735 | }); | ||
736 | return obj_err; | ||
737 | } | ||
738 | |||
739 | env->buf[index].value = obj_duplicate(value); | ||
740 | return obj_nil; | ||
741 | } | 734 | } |
742 | 735 | ||
743 | Object * | 736 | Object * |
@@ -757,11 +750,11 @@ proc_lambda(Environment *env, Object *obj) { | |||
757 | }); | 750 | }); |
758 | return obj_err; | 751 | return obj_err; |
759 | } | 752 | } |
760 | Object *body = obj->cdr->car; | 753 | Object *body = obj->cdr; |
761 | Object *fun = alloc_object(OBJ_TYPE_LAMBDA); | 754 | Object *fun = alloc_object(OBJ_TYPE_LAMBDA); |
762 | fun->args = obj_duplicate(args); | 755 | fun->args = obj_duplicate(args); |
763 | fun->body = obj_duplicate(body); | 756 | fun->body = obj_duplicate(body); |
764 | fun->env = env_create(env); | 757 | fun->env = env; |
765 | return fun; | 758 | return fun; |
766 | } | 759 | } |
767 | 760 | ||