]> git.lizzy.rs Git - nothing.git/blob - src/script/parser.c
(#323) Numbers are long ints
[nothing.git] / src / script / parser.c
1 #include <assert.h>
2 #include <ctype.h>
3 #include <errno.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <inttypes.h>
9
10 #include "script/parser.h"
11 #include "system/lt.h"
12 #include "system/lt/lt_adapters.h"
13
14 #define MAX_BUFFER_LENGTH (5 * 1000 * 1000)
15
16 static struct ParseResult parse_expr(Gc *gc, struct Token current_token);
17
18 static struct ParseResult parse_cdr(Gc *gc, struct Token current_token)
19 {
20     if (*current_token.begin != '.') {
21         return parse_failure("Expected .", current_token.begin);
22     }
23
24     struct ParseResult cdr = read_expr_from_string(gc, current_token.end);
25     if (cdr.is_error) {
26         return cdr;
27     }
28
29     current_token = next_token(cdr.end);
30
31     if (*current_token.begin != ')') {
32         return parse_failure("Expected )", current_token.begin);
33     }
34
35     return parse_success(cdr.expr, current_token.end);
36 }
37
38 static struct ParseResult parse_list_end(Gc *gc, struct Token current_token)
39 {
40     if (*current_token.begin != ')') {
41         return parse_failure("Expected )", current_token.begin);
42     }
43
44     return parse_success(atom_as_expr(create_symbol_atom(gc, "nil", NULL)),
45                          current_token.end);
46 }
47
48 static struct ParseResult parse_list(Gc *gc, struct Token current_token)
49 {
50     if (*current_token.begin != '(') {
51         return parse_failure("Expected (", current_token.begin);
52     }
53
54     current_token = next_token(current_token.end);
55
56     if (*current_token.begin == ')') {
57         return parse_list_end(gc, current_token);
58     }
59
60     struct ParseResult car = parse_expr(gc, current_token);
61     if (car.is_error) {
62         return car;
63     }
64
65     struct Cons *list = create_cons(gc, car.expr, void_expr());
66     struct Cons *cons = list;
67     current_token = next_token(car.end);
68
69     while (*current_token.begin != '.' &&
70            *current_token.begin != ')' &&
71            *current_token.begin != 0) {
72         car = parse_expr(gc, current_token);
73         if (car.is_error) {
74             return car;
75         }
76
77         cons->cdr = cons_as_expr(create_cons(gc, car.expr, void_expr()));
78         cons = cons->cdr.cons;
79
80         current_token = next_token(car.end);
81     }
82
83     struct ParseResult cdr = *current_token.begin == '.'
84         ? parse_cdr(gc, current_token)
85         : parse_list_end(gc, current_token);
86
87     if (cdr.is_error) {
88         return cdr;
89     }
90
91     cons->cdr = cdr.expr;
92
93     return parse_success(cons_as_expr(list), cdr.end);
94 }
95
96 static struct ParseResult parse_string(Gc *gc, struct Token current_token)
97 {
98     if (*current_token.begin != '"') {
99         return parse_failure("Expected \"", current_token.begin);
100     }
101
102     if (*(current_token.end - 1) != '"') {
103         return parse_failure("Unclosed string", current_token.begin);
104     }
105
106     if (current_token.begin + 1 == current_token.end) {
107         return parse_success(atom_as_expr(create_string_atom(gc, "", NULL)),
108                              current_token.end);
109     }
110
111     return parse_success(
112         atom_as_expr(
113             create_string_atom(gc, current_token.begin + 1, current_token.end - 1)),
114         current_token.end);
115 }
116
117 static struct ParseResult parse_number(Gc *gc, struct Token current_token)
118 {
119     char *endptr = 0;
120     const long int x = strtoimax(current_token.begin, &endptr, 10);
121
122     if (current_token.begin == endptr || current_token.end != endptr) {
123         return parse_failure("Expected number", current_token.begin);
124     }
125
126     return parse_success(
127         atom_as_expr(create_number_atom(gc, x)),
128         current_token.end);
129 }
130
131 static struct ParseResult parse_symbol(Gc *gc, struct Token current_token)
132 {
133     if (*current_token.begin == 0) {
134         return parse_failure("EOF", current_token.begin);
135     }
136
137     return parse_success(
138         atom_as_expr(create_symbol_atom(gc, current_token.begin, current_token.end)),
139         current_token.end);
140 }
141
142 static struct ParseResult parse_expr(Gc *gc, struct Token current_token)
143 {
144     if (*current_token.begin == 0) {
145         return parse_failure("EOF", current_token.begin);
146     }
147
148     switch (*current_token.begin) {
149     case '(': return parse_list(gc, current_token);
150     /* TODO(#292): parser does not support escaped string characters */
151     case '"': return parse_string(gc, current_token);
152     default: {}
153     }
154
155     if (isdigit(*current_token.begin)) {
156         return parse_number(gc, current_token);
157     }
158
159     return parse_symbol(gc, current_token);
160 }
161
162 struct ParseResult read_expr_from_string(Gc *gc, const char *str)
163 {
164     assert(str);
165     return parse_expr(gc, next_token(str));
166 }
167
168 struct ParseResult read_expr_from_file(Gc *gc, const char *filename)
169 {
170     assert(filename);
171
172     Lt *lt = create_lt();
173     if (lt == NULL) {
174         return parse_failure("Could not create Lt object", NULL);
175     }
176
177     FILE *stream = PUSH_LT(lt, fopen(filename, "rb"), fclose_lt);
178     if (!stream) {
179         /* TODO(#307): ParseResult should not be used for reporting IO failures */
180         RETURN_LT(lt, parse_failure(strerror(errno), NULL));
181     }
182
183     if (fseek(stream, 0, SEEK_END) != 0) {
184         RETURN_LT(lt, parse_failure("Could not find the end of the file", NULL));
185     }
186
187     const long int buffer_length = ftell(stream);
188
189     if (buffer_length < 0) {
190         RETURN_LT(lt, parse_failure("Couldn't get the size of file", NULL));
191     }
192
193     if (buffer_length == 0) {
194         RETURN_LT(lt, parse_failure("File is empty", NULL));
195     }
196
197     if (buffer_length >= MAX_BUFFER_LENGTH) {
198         RETURN_LT(lt, parse_failure("File is too big", NULL));
199     }
200
201     if (fseek(stream, 0, SEEK_SET) != 0) {
202         RETURN_LT(lt, parse_failure("Could not find the beginning of the file", NULL));
203     }
204
205     char * const buffer = PUSH_LT(lt, malloc((size_t) buffer_length + 1), free);
206     if (buffer == NULL) {
207         RETURN_LT(lt, parse_failure(strerror(errno), NULL));
208     }
209
210     if (fread(buffer, 1, (size_t) buffer_length, stream) != (size_t) buffer_length) {
211         RETURN_LT(lt, parse_failure("Could not read the file", NULL));
212     }
213
214     struct ParseResult result = read_expr_from_string(gc, buffer);
215
216     RETURN_LT(lt, result);
217 }
218
219 struct ParseResult parse_success(struct Expr expr,
220                                  const char *end)
221 {
222     struct ParseResult result = {
223         .is_error = false,
224         .expr = expr,
225         .end = end
226     };
227
228     return result;
229 }
230
231 struct ParseResult parse_failure(const char *error_message,
232                                  const char *end)
233 {
234     struct ParseResult result = {
235         .is_error = true,
236         .error_message = error_message,
237         .end = end
238     };
239
240     return result;
241 }
242
243 void print_parse_error(FILE *stream,
244                        const char *str,
245                        struct ParseResult result)
246 {
247     /* TODO(#294): print_parse_error doesn't support multiple lines */
248     if (!result.is_error) {
249         return;
250     }
251
252     if (result.end) {
253         fprintf(stream, "%s\n", str);
254         for (size_t i = 0; i < (size_t) (result.end - str); ++i) {
255             fprintf(stream, " ");
256         }
257         fprintf(stream, "^\n");
258     }
259
260     fprintf(stream, "%s\n", result.error_message);
261 }