1 #include "system/stacktrace.h"
10 #include "ebisp/builtins.h"
11 #include "ebisp/parser.h"
12 #include "system/lt.h"
14 #define MAX_BUFFER_LENGTH (5 * 1000 * 1000)
16 static void fclose_lt(void* file)
21 static struct ParseResult parse_expr(Gc *gc, struct Token current_token);
23 static struct ParseResult parse_cdr(Gc *gc, struct Token current_token)
25 if (*current_token.begin != '.') {
26 return parse_failure("Expected .", current_token.begin);
29 struct ParseResult cdr = read_expr_from_string(gc, current_token.end);
34 current_token = next_token(cdr.end);
36 if (*current_token.begin != ')') {
37 return parse_failure("Expected )", current_token.begin);
40 return parse_success(cdr.expr, current_token.end);
43 static struct ParseResult parse_list_end(Gc *gc, struct Token current_token)
45 if (*current_token.begin != ')') {
46 return parse_failure("Expected )", current_token.begin);
49 return parse_success(atom_as_expr(create_symbol_atom(gc, "nil", NULL)),
53 static struct ParseResult parse_list(Gc *gc, struct Token current_token)
55 if (*current_token.begin != '(') {
56 return parse_failure("Expected (", current_token.begin);
59 current_token = next_token(current_token.end);
61 if (*current_token.begin == ')') {
62 return parse_list_end(gc, current_token);
65 struct ParseResult car = parse_expr(gc, current_token);
70 struct Cons *list = create_cons(gc, car.expr, void_expr());
71 struct Cons *cons = list;
72 current_token = next_token(car.end);
74 while (*current_token.begin != '.' &&
75 *current_token.begin != ')' &&
76 *current_token.begin != 0) {
77 car = parse_expr(gc, current_token);
82 cons->cdr = cons_as_expr(create_cons(gc, car.expr, void_expr()));
83 cons = cons->cdr.cons;
85 current_token = next_token(car.end);
88 struct ParseResult cdr = *current_token.begin == '.'
89 ? parse_cdr(gc, current_token)
90 : parse_list_end(gc, current_token);
98 return parse_success(cons_as_expr(list), cdr.end);
101 static struct ParseResult parse_string(Gc *gc, struct Token current_token)
103 if (*current_token.begin != '"') {
104 return parse_failure("Expected \"", current_token.begin);
107 if (*(current_token.end - 1) != '"') {
108 return parse_failure("Unclosed string", current_token.begin);
111 if (current_token.begin + 1 == current_token.end) {
112 return parse_success(atom_as_expr(create_string_atom(gc, "", NULL)),
116 return parse_success(
118 create_string_atom(gc, current_token.begin + 1, current_token.end - 1)),
122 static struct ParseResult parse_number(Gc *gc, struct Token current_token)
125 const long int x = strtol(current_token.begin, &endptr, 10);
127 if (current_token.begin == endptr || current_token.end != endptr) {
128 return parse_failure("Expected number", current_token.begin);
131 return parse_success(
132 atom_as_expr(create_number_atom(gc, x)),
136 static struct ParseResult parse_symbol(Gc *gc, struct Token current_token)
138 if (*current_token.begin == 0) {
139 return parse_failure("EOF", current_token.begin);
142 return parse_success(
143 atom_as_expr(create_symbol_atom(gc, current_token.begin, current_token.end)),
147 static struct ParseResult parse_expr(Gc *gc, struct Token current_token)
149 if (*current_token.begin == 0) {
150 return parse_failure("EOF", current_token.begin);
153 switch (*current_token.begin) {
154 case '(': return parse_list(gc, current_token);
155 /* TODO(#292): parser does not support escaped string characters */
156 case '"': return parse_string(gc, current_token);
158 struct ParseResult result = parse_expr(gc, next_token(current_token.end));
160 if (result.is_error) {
164 result.expr = list(gc, "qe", "quote", result.expr);
170 struct ParseResult result = parse_expr(gc, next_token(current_token.end));
172 if (result.is_error) {
176 result.expr = list(gc, "qe", "quasiquote", result.expr);
182 struct ParseResult result = parse_expr(gc, next_token(current_token.end));
184 if (result.is_error) {
188 result.expr = list(gc, "qe", "unquote", result.expr);
196 if (*current_token.begin == '-' || isdigit(*current_token.begin)) {
197 struct ParseResult result = parse_number(gc, current_token);
198 if (!result.is_error) {
203 return parse_symbol(gc, current_token);
206 struct ParseResult read_expr_from_string(Gc *gc, const char *str)
210 return parse_expr(gc, next_token(str));
213 struct ParseResult read_all_exprs_from_string(Gc *gc, const char *str)
218 struct Token current_token = next_token(str);
219 if (*current_token.end == 0) {
220 return parse_success(NIL(gc), current_token.end);
223 struct ParseResult parse_result = parse_expr(gc, current_token);
224 if (parse_result.is_error) {
228 struct Cons *head = create_cons(gc, parse_result.expr, void_expr());
229 struct Cons *cons = head;
231 current_token = next_token(parse_result.end);
232 while (*current_token.end != 0) {
233 parse_result = parse_expr(gc, current_token);
234 if (parse_result.is_error) {
238 cons->cdr = CONS(gc, parse_result.expr, void_expr());
239 cons = cons->cdr.cons;
240 current_token = next_token(parse_result.end);
245 return parse_success(cons_as_expr(head), parse_result.end);
248 struct ParseResult read_expr_from_file(Gc *gc, const char *filename)
250 trace_assert(filename);
252 Lt *lt = create_lt();
254 FILE *stream = PUSH_LT(lt, fopen(filename, "rb"), fclose_lt);
256 /* TODO(#307): ParseResult should not be used for reporting IO failures */
257 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
260 if (fseek(stream, 0, SEEK_END) != 0) {
261 RETURN_LT(lt, parse_failure("Could not find the end of the file", NULL));
264 const long int buffer_length = ftell(stream);
266 if (buffer_length < 0) {
267 RETURN_LT(lt, parse_failure("Couldn't get the size of file", NULL));
270 if (buffer_length == 0) {
271 RETURN_LT(lt, parse_failure("File is empty", NULL));
274 if (buffer_length >= MAX_BUFFER_LENGTH) {
275 RETURN_LT(lt, parse_failure("File is too big", NULL));
278 if (fseek(stream, 0, SEEK_SET) != 0) {
279 RETURN_LT(lt, parse_failure("Could not find the beginning of the file", NULL));
282 char * const buffer = PUSH_LT(lt, malloc((size_t) buffer_length + 1), free);
283 if (buffer == NULL) {
284 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
287 if (fread(buffer, 1, (size_t) buffer_length, stream) != (size_t) buffer_length) {
288 RETURN_LT(lt, parse_failure("Could not read the file", NULL));
291 struct ParseResult result = read_expr_from_string(gc, buffer);
293 RETURN_LT(lt, result);
296 /* TODO(#598): duplicate code in read_all_exprs_from_file and read_expr_from_file */
297 struct ParseResult read_all_exprs_from_file(Gc *gc, const char *filename)
299 trace_assert(filename);
301 Lt *lt = create_lt();
303 FILE *stream = PUSH_LT(lt, fopen(filename, "rb"), fclose_lt);
305 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
308 if (fseek(stream, 0, SEEK_END) != 0) {
309 RETURN_LT(lt, parse_failure("Could not find the end of the file", NULL));
312 const long int buffer_length = ftell(stream);
314 if (buffer_length < 0) {
315 RETURN_LT(lt, parse_failure("Couldn't get the size of file", NULL));
318 if (buffer_length == 0) {
319 RETURN_LT(lt, parse_failure("File is empty", NULL));
322 if (buffer_length >= MAX_BUFFER_LENGTH) {
323 RETURN_LT(lt, parse_failure("File is too big", NULL));
326 if (fseek(stream, 0, SEEK_SET) != 0) {
327 RETURN_LT(lt, parse_failure("Could not find the beginning of the file", NULL));
330 char * const buffer = PUSH_LT(lt, malloc((size_t) buffer_length + 1), free);
331 if (buffer == NULL) {
332 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
335 if (fread(buffer, 1, (size_t) buffer_length, stream) != (size_t) buffer_length) {
336 RETURN_LT(lt, parse_failure("Could not read the file", NULL));
339 struct ParseResult result = read_all_exprs_from_string(gc, buffer);
341 RETURN_LT(lt, result);
344 struct ParseResult parse_success(struct Expr expr,
347 struct ParseResult result = {
356 struct ParseResult parse_failure(const char *error_message,
359 struct ParseResult result = {
361 .error_message = error_message,
368 void print_parse_error(FILE *stream,
370 struct ParseResult result)
372 /* TODO(#294): print_parse_error doesn't support multiple lines */
373 if (!result.is_error) {
378 fprintf(stream, "%s\n", str);
379 for (size_t i = 0; i < (size_t) (result.end - str); ++i) {
380 fprintf(stream, " ");
382 fprintf(stream, "^\n");
385 fprintf(stream, "%s\n", result.error_message);