9 #include "script/parser.h"
10 #include "system/lt.h"
11 #include "system/lt/lt_adapters.h"
13 #define MAX_BUFFER_LENGTH (5 * 1000 * 1000)
15 static struct ParseResult parse_expr(struct Token current_token);
17 static struct ParseResult parse_cdr(struct Token current_token)
19 if (*current_token.begin != '.') {
20 return parse_failure("Expected .", current_token.begin);
23 struct ParseResult cdr = read_expr_from_string(current_token.end);
28 current_token = next_token(cdr.end);
30 if (*current_token.begin != ')') {
31 destroy_expr(cdr.expr);
32 return parse_failure("Expected )", current_token.begin);
35 return parse_success(cdr.expr, current_token.end);
38 static struct ParseResult parse_list_end(struct Token current_token)
40 if (*current_token.begin != ')') {
41 return parse_failure("Expected )", current_token.begin);
44 return parse_success(atom_as_expr(create_symbol_atom("nil", NULL)),
48 static struct ParseResult parse_list(struct Token current_token)
50 if (*current_token.begin != '(') {
51 return parse_failure("Expected (", current_token.begin);
54 current_token = next_token(current_token.end);
56 if (*current_token.begin == ')') {
57 return parse_list_end(current_token);
60 struct ParseResult car = parse_expr(current_token);
65 struct Cons *list = create_cons(car.expr, void_expr());
66 struct Cons *cons = list;
67 current_token = next_token(car.end);
69 while (*current_token.begin != '.' &&
70 *current_token.begin != ')' &&
71 *current_token.begin != 0) {
72 car = parse_expr(current_token);
78 cons->cdr = cons_as_expr(create_cons(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(current_token)
86 : parse_list_end(current_token);
95 return parse_success(cons_as_expr(list), cdr.end);
98 static struct ParseResult parse_string(struct Token current_token)
100 if (*current_token.begin != '"') {
101 return parse_failure("Expected \"", current_token.begin);
104 if (*(current_token.end - 1) != '"') {
105 return parse_failure("Unclosed string", current_token.begin);
108 if (current_token.begin + 1 == current_token.end) {
109 return parse_success(atom_as_expr(create_string_atom("", NULL)),
113 return parse_success(
115 create_string_atom(current_token.begin + 1, current_token.end - 1)),
119 static struct ParseResult parse_number(struct Token current_token)
122 const float x = strtof(current_token.begin, &endptr);
124 if (current_token.begin == endptr || current_token.end != endptr) {
125 return parse_failure("Expected number", current_token.begin);
128 return parse_success(
129 atom_as_expr(create_number_atom(x)),
133 static struct ParseResult parse_symbol(struct Token current_token)
135 if (*current_token.begin == 0) {
136 return parse_failure("EOF", current_token.begin);
139 return parse_success(
140 atom_as_expr(create_symbol_atom(current_token.begin, current_token.end)),
144 static struct ParseResult parse_expr(struct Token current_token)
146 if (*current_token.begin == 0) {
147 return parse_failure("EOF", current_token.begin);
150 switch (*current_token.begin) {
151 case '(': return parse_list(current_token);
152 /* TODO(#292): parser does not support escaped string characters */
153 case '"': return parse_string(current_token);
157 if (isdigit(*current_token.begin)) {
158 return parse_number(current_token);
161 return parse_symbol(current_token);
164 struct ParseResult read_expr_from_string(const char *str)
167 return parse_expr(next_token(str));
170 struct ParseResult read_expr_from_file(const char *filename)
174 Lt *lt = create_lt();
176 return parse_failure("Could not create Lt object", NULL);
179 FILE *stream = PUSH_LT(lt, fopen(filename, "rb"), fclose_lt);
181 /* TODO(#307): ParseResult should not be used for reporting IO failures */
182 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
185 if (fseek(stream, 0, SEEK_END) != 0) {
186 RETURN_LT(lt, parse_failure("Could not find the end of the file", NULL));
189 const long int buffer_length = ftell(stream);
191 if (buffer_length < 0) {
192 RETURN_LT(lt, parse_failure("Couldn't get the size of file", NULL));
195 if (buffer_length == 0) {
196 RETURN_LT(lt, parse_failure("File is empty", NULL));
199 if (buffer_length >= MAX_BUFFER_LENGTH) {
200 RETURN_LT(lt, parse_failure("File is too big", NULL));
203 if (fseek(stream, 0, SEEK_SET) != 0) {
204 RETURN_LT(lt, parse_failure("Could not find the beginning of the file", NULL));
207 char * const buffer = PUSH_LT(lt, malloc((size_t) buffer_length + 1), free);
208 if (buffer == NULL) {
209 RETURN_LT(lt, parse_failure(strerror(errno), NULL));
212 if (fread(buffer, 1, (size_t) buffer_length, stream) != (size_t) buffer_length) {
213 RETURN_LT(lt, parse_failure("Could not read the file", NULL));
216 struct ParseResult result = read_expr_from_string(buffer);
218 RETURN_LT(lt, result);
221 struct ParseResult parse_success(struct Expr expr,
224 struct ParseResult result = {
233 struct ParseResult parse_failure(const char *error_message,
236 struct ParseResult result = {
238 .error_message = error_message,
245 void print_parse_error(FILE *stream,
247 struct ParseResult result)
249 /* TODO(#294): print_parse_error doesn't support multiple lines */
250 if (!result.is_error) {
255 fprintf(stream, "%s\n", str);
256 for (size_t i = 0; i < (size_t) (result.end - str); ++i) {
257 fprintf(stream, " ");
259 fprintf(stream, "^\n");
262 fprintf(stream, "%s\n", result.error_message);