aboutsummaryrefslogtreecommitdiffstats
path: root/src/treewalk/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/treewalk/main.c')
-rwxr-xr-xsrc/treewalk/main.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/treewalk/main.c b/src/treewalk/main.c
new file mode 100755
index 0000000..a5888fd
--- /dev/null
+++ b/src/treewalk/main.c
@@ -0,0 +1,288 @@
1#include <assert.h>
2#include <getopt.h>
3#include <stdbool.h>
4#include <stdint.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#include "darray.h"
10#include "hashtable.h"
11
12#include "singletons.c"
13
14#include "environment.c"
15#include "errors.c"
16#include "gc.c"
17#include "lexer.c"
18#include "objects.c"
19#include "parser.c"
20#include "primitives.c"
21#include "read_line.c"
22#include "string_view.c"
23
24void
25init(void) {
26 // Initialize garbage collector.
27 gc_init();
28
29 // Initialize singletons.
30 obj_nil = alloc_object(OBJ_TYPE_NIL);
31 obj_true = alloc_object(OBJ_TYPE_BOOL);
32 obj_false = alloc_object(OBJ_TYPE_BOOL);
33 obj_err = alloc_object(OBJ_TYPE_ERR);
34 obj_quote = make_symbol((StringView){"quote", 5});
35 proc_if = alloc_object(OBJ_TYPE_ERR);
36 push_root(obj_nil);
37 push_root(obj_true);
38 push_root(obj_false);
39 push_root(obj_err);
40 push_root(obj_quote);
41 push_root(proc_if);
42
43 // Global environment.
44 global_env = env_create(NULL);
45 // TODO: make sure we create symbols and strings only once (interning
46 // strings?)
47 push_active_env(global_env);
48
49 // Primitive symbols.
50 MAKE_ENV_VAR(global_env, "else", obj_true);
51 MAKE_ENV_VAR(global_env, "nil", obj_nil);
52 MAKE_ENV_VAR(global_env, "if", proc_if);
53
54 // Primitive procedures.
55 MAKE_ENV_PROC(global_env, "eval", proc_eval);
56 MAKE_ENV_PROC(global_env, "quote", proc_quote);
57 MAKE_ENV_PROC(global_env, "car", proc_car);
58 MAKE_ENV_PROC(global_env, "cdr", proc_cdr);
59 MAKE_ENV_PROC(global_env, "cons", proc_cons);
60 MAKE_ENV_PROC(global_env, "list", proc_list);
61 MAKE_ENV_PROC(global_env, "+", proc_sum);
62 MAKE_ENV_PROC(global_env, "-", proc_sub);
63 MAKE_ENV_PROC(global_env, "*", proc_mul);
64 MAKE_ENV_PROC(global_env, "/", proc_div);
65 MAKE_ENV_PROC(global_env, "%", proc_mod);
66 MAKE_ENV_PROC(global_env, "print", proc_print);
67 MAKE_ENV_PROC(global_env, "display", proc_display);
68 MAKE_ENV_PROC(global_env, "newline", proc_newline);
69 MAKE_ENV_PROC(global_env, "boolean?", proc_is_boolean);
70 MAKE_ENV_PROC(global_env, "nil?", proc_is_nil);
71 MAKE_ENV_PROC(global_env, "symbol?", proc_is_symbol);
72 MAKE_ENV_PROC(global_env, "string?", proc_is_string);
73 MAKE_ENV_PROC(global_env, "fixnum?", proc_is_fixnum);
74 MAKE_ENV_PROC(global_env, "pair?", proc_is_pair);
75 MAKE_ENV_PROC(global_env, "procedure?", proc_is_procedure);
76 MAKE_ENV_PROC(global_env, "error?", proc_is_error);
77 MAKE_ENV_PROC(global_env, "not", proc_not);
78 MAKE_ENV_PROC(global_env, "and", proc_and);
79 MAKE_ENV_PROC(global_env, "or", proc_or);
80 MAKE_ENV_PROC(global_env, "cond", proc_cond);
81 MAKE_ENV_PROC(global_env, "<", proc_num_less_than);
82 MAKE_ENV_PROC(global_env, "<=", proc_num_lesseq_than);
83 MAKE_ENV_PROC(global_env, ">", proc_num_greater_than);
84 MAKE_ENV_PROC(global_env, ">=", proc_num_greatereq_than);
85 MAKE_ENV_PROC(global_env, "=", proc_num_equal);
86 MAKE_ENV_PROC(global_env, "eq?", proc_equal);
87 MAKE_ENV_PROC(global_env, "def", proc_define);
88 MAKE_ENV_PROC(global_env, "set!", proc_set);
89 MAKE_ENV_PROC(global_env, "lambda", proc_lambda);
90 MAKE_ENV_PROC(global_env, "fun", proc_fun);
91
92 // Runtime procedures.
93 MAKE_ENV_PROC(global_env, "supress-errors", proc_supress_errors);
94}
95
96void
97process_source(const StringView *source) {
98 Token *tokens = tokenize(source);
99 if (errors_n != 0) {
100 if (tokens != NULL) {
101 array_free(tokens);
102 }
103 return;
104 }
105
106 Visitor visitor = (Visitor){
107 .tokens = tokens,
108 .current = 0,
109 };
110 while (has_next_token(&visitor) && peek_token(&visitor).type != TOKEN_EOF) {
111 // Check the root node stack size before parsing
112 size_t root_stack_size = array_size(gc.roots);
113 Object *root = parse_tree(&visitor);
114 array_head(gc.roots)->size = root_stack_size;
115 if (root == obj_err || errors_n != 0) {
116 break;
117 }
118 push_root(root);
119
120 Object *result = eval(global_env, root);
121 if (result != obj_nil) {
122 display(result);
123 printf("\n");
124 }
125 pop_root();
126 }
127
128 if (tokens != NULL) {
129 array_free(tokens);
130 }
131}
132
133#define REPL_PROMPT "bdl> "
134
135void
136run_repl(void) {
137 printf("BDL REPL (Press Ctrl-D or Ctrl-C to exit)\n");
138 while (true) {
139 printf(REPL_PROMPT);
140 StringView sv = read_line();
141 if (sv.start == NULL) {
142 return;
143 }
144 process_source(&sv);
145
146 // Check if there were any errors.
147 if (errors_n != 0 && !supress_errors) {
148 for (size_t i = 0; i < errors_n; i++) {
149 Error err = errors[i];
150 for (size_t j = 0; j < err.col + sizeof(REPL_PROMPT) - 2; j++) {
151 putchar(' ');
152 }
153 printf("|\n");
154 for (size_t j = 0; j < err.col + sizeof(REPL_PROMPT) - 2; j++) {
155 putchar(' ');
156 }
157 printf("%s\n", error_msgs[err.value]);
158 }
159 errors_n = 0;
160 continue;
161 }
162 }
163}
164
165void
166run_file(char *file_name) {
167 FILE *file = fopen(file_name, "r");
168 if (!file) {
169 fprintf(stderr, "error: couldn't open input file: %s\n", file_name);
170 exit(EXIT_FAILURE);
171 }
172
173 // Read entire file into memory.
174 fseek(file, 0, SEEK_END);
175 size_t file_size = ftell(file);
176 fseek(file, 0, SEEK_SET);
177
178 char *source = malloc(file_size + 1);
179 fread(source, 1, file_size, file);
180 source[file_size] = 0;
181
182 StringView sv = (StringView){
183 .start = source,
184 .n = file_size,
185 };
186
187 process_source(&sv);
188
189 // Check if there were any errors.
190 if (errors_n != 0 && !supress_errors) {
191 for (size_t i = 0; i < errors_n; i++) {
192 Error err = errors[i];
193 fprintf(stderr, "%s", file_name);
194 if (err.line != 0) {
195 fprintf(stderr, ":%ld:%ld", err.line, err.col);
196 }
197 fprintf(stderr, ": %s\n", error_msgs[err.value]);
198 }
199 errors_n = 0;
200 }
201
202 free(source);
203 fclose(file);
204}
205
206#define STDIN_BUF_CAP 16
207
208void
209run_stdin(void) {
210 size_t buf_size = 0;
211 char *source = NULL;
212 array_init(source, STDIN_BUF_CAP);
213
214 char c;
215 while ((c = getchar()) != EOF) {
216 array_push(source, c);
217 buf_size++;
218 }
219
220 StringView sv = (StringView){
221 .start = source,
222 .n = buf_size,
223 };
224
225 process_source(&sv);
226
227 // Check if there were any errors.
228 if (errors_n != 0 && !supress_errors) {
229 for (size_t i = 0; i < errors_n; i++) {
230 Error err = errors[i];
231 fprintf(stderr, "stdin");
232 if (err.line != 0) {
233 fprintf(stderr, ":%ld:%ld", err.line, err.col);
234 }
235 fprintf(stderr, ": %s\n", error_msgs[err.value]);
236 }
237 errors_n = 0;
238 }
239
240 array_free(source);
241}
242
243#ifndef BIN_NAME
244#define BIN_NAME "bdl"
245#endif
246
247void
248print_usage(void) {
249 printf("Usage: %s [options] <filename filename ...>\n", BIN_NAME);
250 printf("\n");
251 printf("\t-i\tInteractive mode (REPL).\n");
252 printf("\n");
253}
254
255int
256main(int argc, char *argv[]) {
257 init();
258
259 int option;
260 while ((option = getopt(argc, argv, "i")) != -1) {
261 switch (option) {
262 case 'i': {
263 // Interactive mode.
264 run_repl();
265 return EXIT_SUCCESS;
266 } break;
267 default: {
268 print_usage();
269 return EXIT_FAILURE;
270 } break;
271 }
272 }
273
274 // Run from stdin.
275 if (optind == argc) {
276 run_stdin();
277 return EXIT_SUCCESS;
278 }
279
280 // Run from file.
281 while (optind < argc) {
282 char *file_name = argv[optind];
283 run_file(file_name);
284 optind++;
285 }
286
287 return EXIT_SUCCESS;
288}