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 void fclose_lt(void* file)
22 static struct ParseResult parse_expr(Gc *gc, struct Token current_token);
24 static struct ParseResult parse_cdr(Gc *gc, struct Token current_token)
26 if (*current_token.begin != '.') {
27 return parse_failure("Expected .", current_token.begin);
30 struct ParseResult cdr = read_expr_from_string(gc, current_token.end);
35 current_token = next_token(cdr.end);
37 if (*current_token.begin != ')') {
38 return parse_failure("Expected )", current_token.begin);
41 return parse_success(cdr.expr, current_token.end);
44 static struct ParseResult parse_list_end(Gc *gc, struct Token current_token)
46 if (*current_token.begin != ')') {
47 return parse_failure("Expected )", current_token.begin);
50 return parse_success(atom_as_expr(create_symbol_atom(gc, "nil", NULL)),
54 static struct ParseResult parse_list(Gc *gc, struct Token current_token)
56 if (*current_token.begin != '(') {
57 return parse_failure("Expected (", current_token.begin);
60 current_token = next_token(current_token.end);
62 if (*current_token.begin == ')') {
63 return parse_list_end(gc, current_token);
66 struct ParseResult car = parse_expr(gc, current_token);
71 struct Cons *list = create_cons(gc, car.expr, void_expr());
72 struct Cons *cons = list;
73 current_token = next_token(car.end);
75 while (*current_token.begin != '.' &&
76 *current_token.begin != ')' &&
77 *current_token.begin != 0) {
78 car = parse_expr(gc, current_token);
83 cons->cdr = cons_as_expr(create_cons(gc, car.expr, void_expr()));
84 cons = cons->cdr.cons;
86 current_token = next_token(car.end);
89 struct ParseResult cdr = *current_token.begin == '.'
90 ? parse_cdr(gc, current_token)
91 : parse_list_end(gc, current_token);
99 return parse_success(cons_as_expr(list), cdr.end);
102 static struct ParseResult parse_string(Gc *gc, struct Token current_token)
104 if (*current_token.begin != '"') {
105 return parse_failure("Expected \"", current_token.begin);
108 if (*(current_token.end - 1) != '"') {
109 return parse_failure("Unclosed string", current_token.begin);
112 if (current_token.begin + 1 == current_token.end) {
113 return parse_success(atom_as_expr(create_string_atom(gc, "", NULL)),
117 return parse_success(
119 create_string_atom(gc, current_token.begin + 1, current_token.end - 1)),
123 static struct ParseResult parse_number(Gc *gc, struct Token current_token)
126 const long int x = strtoimax(current_token.begin, &endptr, 10);
128 if (current_token.begin == endptr || current_token.end != endptr) {
129 return parse_failure("Expected number", current_token.begin);
132 return parse_success(
133 atom_as_expr(create_number_atom(gc, x)),
137 static struct ParseResult parse_symbol(Gc *gc, struct Token current_token)
139 if (*current_token.begin == 0) {
140 return parse_failure("EOF", current_token.begin);
143 return parse_success(
144 atom_as_expr(create_symbol_atom(gc, current_token.begin, current_token.end)),
148 static struct ParseResult parse_expr(Gc *gc, struct Token current_token)
150 if (*current_token.begin == 0) {
151 return parse_failure("EOF", current_token.begin);
154 switch (*current_token.begin) {
155 case '(': return parse_list(gc, current_token);
156 /* TODO(#292): parser does not support escaped string characters */
157 case '"': return parse_string(gc, current_token);
159 struct ParseResult result = parse_expr(gc, next_token(current_token.end));
161 if (result.is_error) {
165 result.expr = list(gc, "qe", "quote", result.expr);
171 struct ParseResult result = parse_expr(gc, next_token(current_token.end));
173 if (result.is_error) {
177 result.expr = list(gc, "qe", "quasiquote", result.expr);
183 struct ParseResult result = parse_expr(gc, next_token(current_token.end));
185 if (result.is_error) {
189 result.expr = list(gc, "qe", "unquote", result.expr);
197 if (*current_token.begin == '-' || isdigit(*current_token.begin)) {
198 struct ParseResult result = parse_number(gc, current_token);
199 if (!result.is_error) {
204 return parse_symbol(gc, current_token);
207 struct ParseResult read_expr_from_string(Gc *gc, const char *str)
211 return parse_expr(gc, next_token(str));
214 struct ParseResult read_all_exprs_from_string(Gc *gc, const char *str)
219 struct Token current_token = next_token(str);
220 struct ParseResult parse_result = parse_expr(gc, current_token);
221 if (parse_result.is_error) {
225 struct Cons *head = create_cons(gc, parse_result.expr, void_expr());
226 struct Cons *cons = head;
228 current_token = next_token(parse_result.end);
229 while (*current_token.end != 0) {
230 parse_result = parse_expr(gc, current_token);
231 if (parse_result.is_error) {
235 cons->cdr = CONS(gc, parse_result.expr, void_expr());
236 cons = cons->cdr.cons;
237 current_token = next_token(parse_result.end);
242 return parse_success(cons_as_expr(head), parse_result.end);
245 struct ParseResult read_expr_from_file(Gc *gc, const char *filename)
247 trace_assert(filename);
249 Lt *lt = create_lt();
251 FILE *stream = PUSH_LT(lt, fopen(filename, "rb"), fclose_lt);
253 /* TODO(#307): ParseResult should not be used for reporting IO failures */
254 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
257 if (fseek(stream, 0, SEEK_END) != 0) {
258 RETURN_LT(lt, parse_failure("Could not find the end of the file", NULL));
261 const long int buffer_length = ftell(stream);
263 if (buffer_length < 0) {
264 RETURN_LT(lt, parse_failure("Couldn't get the size of file", NULL));
267 if (buffer_length == 0) {
268 RETURN_LT(lt, parse_failure("File is empty", NULL));
271 if (buffer_length >= MAX_BUFFER_LENGTH) {
272 RETURN_LT(lt, parse_failure("File is too big", NULL));
275 if (fseek(stream, 0, SEEK_SET) != 0) {
276 RETURN_LT(lt, parse_failure("Could not find the beginning of the file", NULL));
279 char * const buffer = PUSH_LT(lt, malloc((size_t) buffer_length + 1), free);
280 if (buffer == NULL) {
281 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
284 if (fread(buffer, 1, (size_t) buffer_length, stream) != (size_t) buffer_length) {
285 RETURN_LT(lt, parse_failure("Could not read the file", NULL));
288 struct ParseResult result = read_expr_from_string(gc, buffer);
290 RETURN_LT(lt, result);
293 /* TODO(#598): duplicate code in read_all_exprs_from_file and read_expr_from_file */
294 struct ParseResult read_all_exprs_from_file(Gc *gc, const char *filename)
296 trace_assert(filename);
298 Lt *lt = create_lt();
300 FILE *stream = PUSH_LT(lt, fopen(filename, "rb"), fclose_lt);
302 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
305 if (fseek(stream, 0, SEEK_END) != 0) {
306 RETURN_LT(lt, parse_failure("Could not find the end of the file", NULL));
309 const long int buffer_length = ftell(stream);
311 if (buffer_length < 0) {
312 RETURN_LT(lt, parse_failure("Couldn't get the size of file", NULL));
315 if (buffer_length == 0) {
316 RETURN_LT(lt, parse_failure("File is empty", NULL));
319 if (buffer_length >= MAX_BUFFER_LENGTH) {
320 RETURN_LT(lt, parse_failure("File is too big", NULL));
323 if (fseek(stream, 0, SEEK_SET) != 0) {
324 RETURN_LT(lt, parse_failure("Could not find the beginning of the file", NULL));
327 char * const buffer = PUSH_LT(lt, malloc((size_t) buffer_length + 1), free);
328 if (buffer == NULL) {
329 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
332 if (fread(buffer, 1, (size_t) buffer_length, stream) != (size_t) buffer_length) {
333 RETURN_LT(lt, parse_failure("Could not read the file", NULL));
336 struct ParseResult result = read_all_exprs_from_string(gc, buffer);
338 RETURN_LT(lt, result);
341 struct ParseResult parse_success(struct Expr expr,
344 struct ParseResult result = {
353 struct ParseResult parse_failure(const char *error_message,
356 struct ParseResult result = {
358 .error_message = error_message,
365 void print_parse_error(FILE *stream,
367 struct ParseResult result)
369 /* TODO(#294): print_parse_error doesn't support multiple lines */
370 if (!result.is_error) {
375 fprintf(stream, "%s\n", str);
376 for (size_t i = 0; i < (size_t) (result.end - str); ++i) {
377 fprintf(stream, " ");
379 fprintf(stream, "^\n");
382 fprintf(stream, "%s\n", result.error_message);