]> git.lizzy.rs Git - nothing.git/blob - src/script/interpreter.c
(#328) Make scope more typesafe
[nothing.git] / src / script / interpreter.c
1 #include <assert.h>
2 #include <math.h>
3 #include <string.h>
4
5 #include "./builtins.h"
6 #include "./expr.h"
7 #include "./interpreter.h"
8 #include "./scope.h"
9
10 struct EvalResult eval_success(struct Expr expr)
11 {
12     struct EvalResult result = {
13         .is_error = false,
14         .expr = expr,
15     };
16
17     return result;
18 }
19
20 struct EvalResult eval_failure(struct Expr error)
21 {
22     struct EvalResult result = {
23         .is_error = true,
24         .expr = error,
25     };
26
27     return result;
28 }
29
30 static struct EvalResult length(Gc *gc, struct Expr obj)
31 {
32     if (!list_p(obj)) {
33         return eval_failure(list(gc, 3,
34                                  SYMBOL(gc, "wrong-argument-type"),
35                                  SYMBOL(gc, "listp"),
36                                  obj));
37     }
38
39     long int count = 0;
40
41     while (!nil_p(obj)) {
42         count++;
43         obj = obj.cons->cdr;
44     }
45
46     return eval_success(NUMBER(gc, count));
47 }
48
49 static struct EvalResult eval_atom(Gc *gc, struct Scope *scope, struct Atom *atom)
50 {
51     (void) scope;
52     (void) gc;
53
54     switch (atom->type) {
55     case ATOM_NUMBER:
56     case ATOM_STRING:
57         return eval_success(atom_as_expr(atom));
58
59     case ATOM_SYMBOL: {
60         if (nil_p(atom_as_expr(atom))) {
61             return eval_success(atom_as_expr(atom));
62         }
63
64         struct Expr value = get_scope_value(scope, atom_as_expr(atom));
65
66         if (nil_p(value)) {
67             return eval_failure(CONS(gc,
68                                      SYMBOL(gc, "void-variable"),
69                                      atom_as_expr(atom)));
70         }
71
72         return eval_success(value.cons->cdr);
73     }
74     }
75
76     return eval_failure(CONS(gc,
77                              SYMBOL(gc, "unexpected-expression"),
78                              atom_as_expr(atom)));
79 }
80
81 static struct EvalResult eval_all_args(Gc *gc, struct Scope *scope, struct Expr args)
82 {
83     (void) scope;
84     (void) args;
85
86     switch(args.type) {
87     case EXPR_ATOM:
88         return eval_atom(gc, scope, args.atom);
89
90     case EXPR_CONS: {
91         struct EvalResult car = eval(gc, scope, args.cons->car);
92         if (car.is_error) {
93             return car;
94         }
95
96         struct EvalResult cdr = eval_all_args(gc, scope, args.cons->cdr);
97         if (cdr.is_error) {
98             return cdr;
99         }
100
101         return eval_success(cons_as_expr(create_cons(gc, car.expr, cdr.expr)));
102     }
103
104     default: {}
105     }
106
107     return eval_failure(CONS(gc,
108                              SYMBOL(gc, "unexpected-expression"),
109                              args));
110 }
111
112 static struct EvalResult plus_op(Gc *gc, struct Expr args)
113 {
114     long int result = 0.0f;
115
116     while (!nil_p(args)) {
117         if (args.type != EXPR_CONS) {
118             return eval_failure(CONS(gc,
119                                      SYMBOL(gc, "expected-cons"),
120                                      args));
121         }
122
123         if (args.cons->car.type != EXPR_ATOM ||
124             args.cons->car.atom->type != ATOM_NUMBER) {
125             return eval_failure(CONS(gc,
126                                      SYMBOL(gc, "expected-number"),
127                                      args.cons->car));
128         }
129
130         result += args.cons->car.atom->num;
131         args = args.cons->cdr;
132     }
133
134     return eval_success(atom_as_expr(create_number_atom(gc, result)));
135 }
136
137 static struct EvalResult eval_funcall(Gc *gc, struct Scope *scope, struct Cons *cons)
138 {
139     assert(cons);
140     (void) scope;
141
142     if (!symbol_p(cons->car)) {
143         return eval_failure(CONS(gc,
144                                  SYMBOL(gc, "expected-symbol"),
145                                  cons->car));
146     }
147
148     /* TODO(#323): set builtin function is not implemented */
149     if (strcmp(cons->car.atom->sym, "+") == 0) {
150         struct EvalResult args = eval_all_args(gc, scope, cons->cdr);
151         if (args.is_error) {
152             return args;
153         }
154         return plus_op(gc, args.expr);
155     } else if (strcmp(cons->car.atom->sym, "set") == 0) {
156         struct Expr args = cons->cdr;
157         struct EvalResult n = length(gc, args);
158
159         if (n.is_error) {
160             return n;
161         }
162
163         if (n.expr.atom->num != 2) {
164             return eval_failure(list(gc, 3,
165                                      SYMBOL(gc, "wrong-number-of-arguments"),
166                                      SYMBOL(gc, "set"),
167                                      NUMBER(gc, n.expr.atom->num)));
168         }
169
170         struct Expr name = args.cons->car;
171         if (!symbol_p(name)) {
172             return eval_failure(list(gc, 3,
173                                      SYMBOL(gc, "wrong-type-argument"),
174                                      SYMBOL(gc, "symbolp"),
175                                      name));
176         }
177
178         struct EvalResult value = eval(gc, scope, args.cons->cdr.cons->car);
179         if (value.is_error) {
180             return value;
181         }
182
183         set_scope_value(gc, scope, name, value.expr);
184
185         return eval_success(value.expr);
186     }
187
188     return eval_failure(CONS(gc,
189                              SYMBOL(gc, "unknown-function"),
190                              cons->car));
191 }
192
193 struct EvalResult eval(Gc *gc, struct Scope *scope, struct Expr expr)
194 {
195     switch(expr.type) {
196     case EXPR_ATOM:
197         return eval_atom(gc, scope, expr.atom);
198
199     case EXPR_CONS:
200         return eval_funcall(gc, scope, expr.cons);
201
202     default: {}
203     }
204
205     return eval_failure(CONS(gc,
206                              SYMBOL(gc, "unexpected-expression"),
207                              expr));
208 }