Commit | Line | Data |
---|---|---|
31690700 JM |
1 | #include <stdlib.h> |
2 | #include <stdio.h> | |
b81b2a7e | 3 | #include <string.h> |
31690700 | 4 | #include <unistd.h> |
8cb5cda4 | 5 | |
31690700 JM |
6 | #include "types.h" |
7 | #include "readline.h" | |
8 | #include "reader.h" | |
9 | ||
10 | // Declarations | |
11 | MalVal *EVAL(MalVal *ast, Env *env); | |
12 | ||
13 | // read | |
14 | MalVal *READ(char prompt[], char *str) { | |
15 | char *line; | |
16 | MalVal *ast; | |
17 | if (str) { | |
18 | line = str; | |
19 | } else { | |
20 | line = _readline(prompt); | |
21 | if (!line) { | |
22 | _error("EOF"); | |
23 | return NULL; | |
24 | } | |
25 | } | |
26 | ast = read_str(line); | |
6b3ecaa7 | 27 | if (!str) { MAL_GC_FREE(line); } |
31690700 JM |
28 | return ast; |
29 | } | |
30 | ||
31 | // eval | |
32 | MalVal *eval_ast(MalVal *ast, Env *env) { | |
33 | if (!ast || mal_error) return NULL; | |
34 | if (ast->type == MAL_SYMBOL) { | |
35 | //g_print("EVAL symbol: %s\n", ast->val.string); | |
b8ee29b2 | 36 | return env_get(env, ast); |
31690700 JM |
37 | } else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) { |
38 | //g_print("EVAL sequential: %s\n", _pr_str(ast,1)); | |
39 | MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env); | |
40 | if (!el || mal_error) return NULL; | |
41 | el->type = ast->type; | |
42 | return el; | |
43 | } else if (ast->type == MAL_HASH_MAP) { | |
44 | //g_print("EVAL hash_map: %s\n", _pr_str(ast,1)); | |
45 | GHashTableIter iter; | |
46 | gpointer key, value; | |
47 | MalVal *seq = malval_new_list(MAL_LIST, | |
48 | g_array_sized_new(TRUE, TRUE, sizeof(MalVal*), | |
49 | _count(ast))); | |
50 | g_hash_table_iter_init (&iter, ast->val.hash_table); | |
51 | while (g_hash_table_iter_next (&iter, &key, &value)) { | |
52 | MalVal *kname = malval_new_string((char *)key); | |
53 | g_array_append_val(seq->val.array, kname); | |
54 | MalVal *new_val = EVAL((MalVal *)value, env); | |
55 | g_array_append_val(seq->val.array, new_val); | |
56 | } | |
8cb5cda4 | 57 | return _hash_map(seq); |
31690700 JM |
58 | } else { |
59 | //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); | |
60 | return ast; | |
61 | } | |
62 | } | |
63 | ||
64 | MalVal *EVAL(MalVal *ast, Env *env) { | |
31690700 | 65 | if (!ast || mal_error) return NULL; |
8cb5cda4 | 66 | //g_print("EVAL: %s\n", _pr_str(ast,1)); |
31690700 JM |
67 | if (ast->type != MAL_LIST) { |
68 | return eval_ast(ast, env); | |
69 | } | |
70 | if (!ast || mal_error) return NULL; | |
71 | ||
72 | // apply list | |
73 | //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); | |
74 | int i, len; | |
75 | if (_count(ast) == 0) { return ast; } | |
76 | MalVal *a0 = _nth(ast, 0); | |
77 | assert_type(a0, MAL_SYMBOL, "Cannot apply %s", _pr_str(a0,1)); | |
78 | if (strcmp("def!", a0->val.string) == 0) { | |
79 | //g_print("eval apply def!\n"); | |
80 | MalVal *a1 = _nth(ast, 1), | |
81 | *a2 = _nth(ast, 2); | |
82 | MalVal *res = EVAL(a2, env); | |
fba3aeb2 | 83 | if (mal_error) return NULL; |
b8ee29b2 | 84 | env_set(env, a1, res); |
31690700 JM |
85 | return res; |
86 | } else if (strcmp("let*", a0->val.string) == 0) { | |
87 | //g_print("eval apply let*\n"); | |
88 | MalVal *a1 = _nth(ast, 1), | |
89 | *a2 = _nth(ast, 2), | |
90 | *key, *val; | |
91 | assert_type(a1, MAL_LIST|MAL_VECTOR, | |
92 | "let* bindings must be list or vector"); | |
93 | len = _count(a1); | |
94 | assert((len % 2) == 0, "odd number of let* bindings forms"); | |
95 | Env *let_env = new_env(env, NULL, NULL); | |
96 | for(i=0; i<len; i+=2) { | |
97 | key = g_array_index(a1->val.array, MalVal*, i); | |
98 | val = g_array_index(a1->val.array, MalVal*, i+1); | |
99 | assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); | |
b8ee29b2 | 100 | env_set(let_env, key, EVAL(val, let_env)); |
31690700 JM |
101 | } |
102 | return EVAL(a2, let_env); | |
103 | } else { | |
104 | //g_print("eval apply\n"); | |
105 | MalVal *el = eval_ast(ast, env); | |
106 | if (!el || mal_error) { return NULL; } | |
8cb5cda4 | 107 | MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))_first(el); |
31690700 JM |
108 | return f(_nth(el, 1), _nth(el, 2)); |
109 | } | |
110 | } | |
111 | ||
112 | ||
113 | char *PRINT(MalVal *exp) { | |
114 | if (mal_error) { | |
31690700 JM |
115 | return NULL; |
116 | } | |
117 | return _pr_str(exp,1); | |
118 | } | |
119 | ||
120 | // repl | |
121 | ||
122 | // read and eval | |
123 | MalVal *RE(Env *env, char *prompt, char *str) { | |
124 | MalVal *ast, *exp; | |
125 | ast = READ(prompt, str); | |
126 | if (!ast || mal_error) return NULL; | |
127 | exp = EVAL(ast, env); | |
128 | if (ast != exp) { | |
129 | malval_free(ast); // Free input structure | |
130 | } | |
131 | return exp; | |
132 | } | |
133 | ||
134 | // Setup the initial REPL environment | |
135 | Env *repl_env; | |
136 | ||
b81b2a7e LB |
137 | WRAP_INTEGER_OP(plus,+) |
138 | WRAP_INTEGER_OP(minus,-) | |
139 | WRAP_INTEGER_OP(multiply,*) | |
140 | WRAP_INTEGER_OP(divide,/) | |
141 | ||
31690700 JM |
142 | void init_repl_env() { |
143 | repl_env = new_env(NULL, NULL, NULL); | |
144 | ||
b8ee29b2 JM |
145 | env_set(repl_env, malval_new_symbol("+"), (MalVal *)int_plus); |
146 | env_set(repl_env, malval_new_symbol("-"), (MalVal *)int_minus); | |
147 | env_set(repl_env, malval_new_symbol("*"), (MalVal *)int_multiply); | |
148 | env_set(repl_env, malval_new_symbol("/"), (MalVal *)int_divide); | |
31690700 JM |
149 | } |
150 | ||
151 | int main() | |
152 | { | |
153 | MalVal *exp; | |
154 | char *output; | |
155 | char prompt[100]; | |
156 | ||
6b3ecaa7 DM |
157 | MAL_GC_SETUP(); |
158 | ||
31690700 JM |
159 | // Set the initial prompt and environment |
160 | snprintf(prompt, sizeof(prompt), "user> "); | |
161 | init_repl_env(); | |
dd7a4f55 | 162 | |
86b689f3 | 163 | // repl loop |
31690700 JM |
164 | for(;;) { |
165 | exp = RE(repl_env, prompt, NULL); | |
166 | if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { | |
167 | return 0; | |
168 | } | |
169 | output = PRINT(exp); | |
170 | ||
dd7a4f55 JM |
171 | if (mal_error) { |
172 | fprintf(stderr, "Error: %s\n", _pr_str(mal_error,1)); | |
173 | malval_free(mal_error); | |
174 | mal_error = NULL; | |
175 | } else if (output) { | |
6b3ecaa7 DM |
176 | puts(output); |
177 | MAL_GC_FREE(output); // Free output string | |
31690700 JM |
178 | } |
179 | ||
180 | //malval_free(exp); // Free evaluated expression | |
181 | } | |
182 | } |