1 #include "system/stacktrace.h"
10 #include "ebisp/builtins.h"
11 #include "ebisp/parser.h"
12 #include "system/lt.h"
13 #include "system/lt/lt_adapters.h"
15 #define MAX_BUFFER_LENGTH (5 * 1000 * 1000)
17 static struct ParseResult parse_expr(Gc *gc, struct Token current_token);
19 static struct ParseResult parse_cdr(Gc *gc, struct Token current_token)
21 if (*current_token.begin != '.') {
22 return parse_failure("Expected .", current_token.begin);
25 struct ParseResult cdr = read_expr_from_string(gc, current_token.end);
30 current_token = next_token(cdr.end);
32 if (*current_token.begin != ')') {
33 return parse_failure("Expected )", current_token.begin);
36 return parse_success(cdr.expr, current_token.end);
39 static struct ParseResult parse_list_end(Gc *gc, struct Token current_token)
41 if (*current_token.begin != ')') {
42 return parse_failure("Expected )", current_token.begin);
45 return parse_success(atom_as_expr(create_symbol_atom(gc, "nil", NULL)),
49 static struct ParseResult parse_list(Gc *gc, struct Token current_token)
51 if (*current_token.begin != '(') {
52 return parse_failure("Expected (", current_token.begin);
55 current_token = next_token(current_token.end);
57 if (*current_token.begin == ')') {
58 return parse_list_end(gc, current_token);
61 struct ParseResult car = parse_expr(gc, current_token);
66 struct Cons *list = create_cons(gc, car.expr, void_expr());
67 struct Cons *cons = list;
68 current_token = next_token(car.end);
70 while (*current_token.begin != '.' &&
71 *current_token.begin != ')' &&
72 *current_token.begin != 0) {
73 car = parse_expr(gc, current_token);
78 cons->cdr = cons_as_expr(create_cons(gc, car.expr, void_expr()));
79 cons = cons->cdr.cons;
81 current_token = next_token(car.end);
84 struct ParseResult cdr = *current_token.begin == '.'
85 ? parse_cdr(gc, current_token)
86 : parse_list_end(gc, current_token);
94 return parse_success(cons_as_expr(list), cdr.end);
97 static struct ParseResult parse_string(Gc *gc, struct Token current_token)
99 if (*current_token.begin != '"') {
100 return parse_failure("Expected \"", current_token.begin);
103 if (*(current_token.end - 1) != '"') {
104 return parse_failure("Unclosed string", current_token.begin);
107 if (current_token.begin + 1 == current_token.end) {
108 return parse_success(atom_as_expr(create_string_atom(gc, "", NULL)),
112 return parse_success(
114 create_string_atom(gc, current_token.begin + 1, current_token.end - 1)),
118 static struct ParseResult parse_number(Gc *gc, struct Token current_token)
121 const long int x = strtoimax(current_token.begin, &endptr, 10);
123 if (current_token.begin == endptr || current_token.end != endptr) {
124 return parse_failure("Expected number", current_token.begin);
127 return parse_success(
128 atom_as_expr(create_number_atom(gc, x)),
132 static struct ParseResult parse_symbol(Gc *gc, struct Token current_token)
134 if (*current_token.begin == 0) {
135 return parse_failure("EOF", current_token.begin);
138 return parse_success(
139 atom_as_expr(create_symbol_atom(gc, current_token.begin, current_token.end)),
143 static struct ParseResult parse_expr(Gc *gc, struct Token current_token)
145 if (*current_token.begin == 0) {
146 return parse_failure("EOF", current_token.begin);
149 switch (*current_token.begin) {
150 case '(': return parse_list(gc, current_token);
151 /* TODO(#292): parser does not support escaped string characters */
152 case '"': return parse_string(gc, current_token);
154 struct ParseResult result = parse_expr(gc, next_token(current_token.end));
156 if (result.is_error) {
160 result.expr = list(gc, "qe", "quote", result.expr);
166 struct ParseResult result = parse_expr(gc, next_token(current_token.end));
168 if (result.is_error) {
172 result.expr = list(gc, "qe", "quasiquote", result.expr);
178 struct ParseResult result = parse_expr(gc, next_token(current_token.end));
180 if (result.is_error) {
184 result.expr = list(gc, "qe", "unquote", result.expr);
192 if (*current_token.begin == '-' || isdigit(*current_token.begin)) {
193 struct ParseResult result = parse_number(gc, current_token);
194 if (!result.is_error) {
199 return parse_symbol(gc, current_token);
202 struct ParseResult read_expr_from_string(Gc *gc, const char *str)
206 return parse_expr(gc, next_token(str));
209 struct ParseResult read_all_exprs_from_string(Gc *gc, const char *str)
214 struct Token current_token = next_token(str);
215 struct ParseResult parse_result = parse_expr(gc, current_token);
216 if (parse_result.is_error) {
220 struct Cons *head = create_cons(gc, parse_result.expr, void_expr());
221 struct Cons *cons = head;
223 current_token = next_token(parse_result.end);
224 while (*current_token.end != 0) {
225 parse_result = parse_expr(gc, current_token);
226 if (parse_result.is_error) {
230 cons->cdr = CONS(gc, parse_result.expr, void_expr());
231 cons = cons->cdr.cons;
232 current_token = next_token(parse_result.end);
237 return parse_success(cons_as_expr(head), parse_result.end);
240 struct ParseResult read_expr_from_file(Gc *gc, const char *filename)
242 trace_assert(filename);
244 Lt *lt = create_lt();
246 return parse_failure("Could not create Lt object", NULL);
249 FILE *stream = PUSH_LT(lt, fopen(filename, "rb"), fclose_lt);
251 /* TODO(#307): ParseResult should not be used for reporting IO failures */
252 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
255 if (fseek(stream, 0, SEEK_END) != 0) {
256 RETURN_LT(lt, parse_failure("Could not find the end of the file", NULL));
259 const long int buffer_length = ftell(stream);
261 if (buffer_length < 0) {
262 RETURN_LT(lt, parse_failure("Couldn't get the size of file", NULL));
265 if (buffer_length == 0) {
266 RETURN_LT(lt, parse_failure("File is empty", NULL));
269 if (buffer_length >= MAX_BUFFER_LENGTH) {
270 RETURN_LT(lt, parse_failure("File is too big", NULL));
273 if (fseek(stream, 0, SEEK_SET) != 0) {
274 RETURN_LT(lt, parse_failure("Could not find the beginning of the file", NULL));
277 char * const buffer = PUSH_LT(lt, malloc((size_t) buffer_length + 1), free);
278 if (buffer == NULL) {
279 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
282 if (fread(buffer, 1, (size_t) buffer_length, stream) != (size_t) buffer_length) {
283 RETURN_LT(lt, parse_failure("Could not read the file", NULL));
286 struct ParseResult result = read_expr_from_string(gc, buffer);
288 RETURN_LT(lt, result);
291 /* TODO(#598): duplicate code in read_all_exprs_from_file and read_expr_from_file */
292 struct ParseResult read_all_exprs_from_file(Gc *gc, const char *filename)
294 trace_assert(filename);
296 Lt *lt = create_lt();
298 return parse_failure("Could not create Lt object", NULL);
301 FILE *stream = PUSH_LT(lt, fopen(filename, "rb"), fclose_lt);
303 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
306 if (fseek(stream, 0, SEEK_END) != 0) {
307 RETURN_LT(lt, parse_failure("Could not find the end of the file", NULL));
310 const long int buffer_length = ftell(stream);
312 if (buffer_length < 0) {
313 RETURN_LT(lt, parse_failure("Couldn't get the size of file", NULL));
316 if (buffer_length == 0) {
317 RETURN_LT(lt, parse_failure("File is empty", NULL));
320 if (buffer_length >= MAX_BUFFER_LENGTH) {
321 RETURN_LT(lt, parse_failure("File is too big", NULL));
324 if (fseek(stream, 0, SEEK_SET) != 0) {
325 RETURN_LT(lt, parse_failure("Could not find the beginning of the file", NULL));
328 char * const buffer = PUSH_LT(lt, malloc((size_t) buffer_length + 1), free);
329 if (buffer == NULL) {
330 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
333 if (fread(buffer, 1, (size_t) buffer_length, stream) != (size_t) buffer_length) {
334 RETURN_LT(lt, parse_failure("Could not read the file", NULL));
337 struct ParseResult result = read_all_exprs_from_string(gc, buffer);
339 RETURN_LT(lt, result);
342 struct ParseResult parse_success(struct Expr expr,
345 struct ParseResult result = {
354 struct ParseResult parse_failure(const char *error_message,
357 struct ParseResult result = {
359 .error_message = error_message,
366 void print_parse_error(FILE *stream,
368 struct ParseResult result)
370 /* TODO(#294): print_parse_error doesn't support multiple lines */
371 if (!result.is_error) {
376 fprintf(stream, "%s\n", str);
377 for (size_t i = 0; i < (size_t) (result.end - str); ++i) {
378 fprintf(stream, " ");
380 fprintf(stream, "^\n");
383 fprintf(stream, "%s\n", result.error_message);