]> git.lizzy.rs Git - nothing.git/blob - src/script/parser.c
(#303) Introduce string and stream readers
[nothing.git] / src / script / parser.c
1 #include <assert.h>
2 #include <ctype.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include "script/parser.h"
7
8 static struct ParseResult parse_expr(struct Token current_token);
9
10 static struct ParseResult parse_cdr(struct Token current_token)
11 {
12     if (*current_token.begin != '.') {
13         return parse_failure("Expected .", current_token.begin);
14     }
15
16     struct ParseResult cdr = read_expr_from_string(current_token.end);
17     if (cdr.is_error) {
18         return cdr;
19     }
20
21     current_token = next_token(cdr.end);
22
23     if (*current_token.begin != ')') {
24         destroy_expr(cdr.expr);
25         return parse_failure("Expected )", current_token.begin);
26     }
27
28     return parse_success(cdr.expr, current_token.end);
29 }
30
31 static struct ParseResult parse_list_end(struct Token current_token)
32 {
33     if (*current_token.begin != ')') {
34         return parse_failure("Expected )", current_token.begin);
35     }
36
37     return parse_success(atom_as_expr(create_symbol_atom("nil", NULL)),
38                          current_token.end);
39 }
40
41 static struct ParseResult parse_list(struct Token current_token)
42 {
43     if (*current_token.begin != '(') {
44         return parse_failure("Expected (", current_token.begin);
45     }
46
47     current_token = next_token(current_token.end);
48
49     if (*current_token.begin == ')') {
50         return parse_list_end(current_token);
51     }
52
53     struct ParseResult car = parse_expr(current_token);
54     if (car.is_error) {
55         return car;
56     }
57
58     struct Cons *list = create_cons(car.expr, void_expr());
59     struct Cons *cons = list;
60     current_token = next_token(car.end);
61
62     while (*current_token.begin != '.' &&
63            *current_token.begin != ')' &&
64            *current_token.begin != 0) {
65         car = parse_expr(current_token);
66         if (car.is_error) {
67             destroy_cons(list);
68             return car;
69         }
70
71         cons->cdr = cons_as_expr(create_cons(car.expr, void_expr()));
72         cons = cons->cdr.cons;
73
74         current_token = next_token(car.end);
75     }
76
77     struct ParseResult cdr = *current_token.begin == '.'
78         ? parse_cdr(current_token)
79         : parse_list_end(current_token);
80
81     if (cdr.is_error) {
82         destroy_cons(list);
83         return cdr;
84     }
85
86     cons->cdr = cdr.expr;
87
88     return parse_success(cons_as_expr(list), cdr.end);
89 }
90
91 static struct ParseResult parse_string(struct Token current_token)
92 {
93     if (*current_token.begin != '"') {
94         return parse_failure("Expected \"", current_token.begin);
95     }
96
97     if (*(current_token.end - 1) != '"') {
98         return parse_failure("Unclosed string", current_token.begin);
99     }
100
101     if (current_token.begin + 1 == current_token.end) {
102         return parse_success(atom_as_expr(create_string_atom("", NULL)),
103                              current_token.end);
104     }
105
106     return parse_success(
107         atom_as_expr(
108             create_string_atom(current_token.begin + 1, current_token.end - 1)),
109         current_token.end);
110 }
111
112 static struct ParseResult parse_number(struct Token current_token)
113 {
114     char *endptr = 0;
115     const float x = strtof(current_token.begin, &endptr);
116
117     if (current_token.begin == endptr || current_token.end != endptr) {
118         return parse_failure("Expected number", current_token.begin);
119     }
120
121     return parse_success(
122         atom_as_expr(create_number_atom(x)),
123         current_token.end);
124 }
125
126 static struct ParseResult parse_symbol(struct Token current_token)
127 {
128     if (*current_token.begin == 0) {
129         return parse_failure("EOF", current_token.begin);
130     }
131
132     return parse_success(
133         atom_as_expr(create_symbol_atom(current_token.begin, current_token.end)),
134         current_token.end);
135 }
136
137 static struct ParseResult parse_expr(struct Token current_token)
138 {
139     if (*current_token.begin == 0) {
140         return parse_failure("EOF", current_token.begin);
141     }
142
143     switch (*current_token.begin) {
144     case '(': return parse_list(current_token);
145     /* TODO(#292): parser does not support escaped string characters */
146     case '"': return parse_string(current_token);
147     default: {}
148     }
149
150     if (isdigit(*current_token.begin)) {
151         return parse_number(current_token);
152     }
153
154     return parse_symbol(current_token);
155 }
156
157 struct ParseResult read_expr_from_string(const char *str)
158 {
159     assert(str);
160     return parse_expr(next_token(str));
161 }
162
163 struct ParseResult read_expr_from_stream(FILE *stream)
164 {
165     assert(stream);
166     return parse_failure("not implemented", NULL);
167 }
168
169 struct ParseResult parse_success(struct Expr expr,
170                                  const char *end)
171 {
172     struct ParseResult result = {
173         .is_error = false,
174         .expr = expr,
175         .end = end
176     };
177
178     return result;
179 }
180
181 struct ParseResult parse_failure(const char *error_message,
182                                  const char *end)
183 {
184     struct ParseResult result = {
185         .is_error = true,
186         .error_message = error_message,
187         .end = end
188     };
189
190     return result;
191 }
192
193 void print_parse_error(FILE *stream,
194                        const char *str,
195                        struct ParseResult result)
196 {
197     /* TODO(#294): print_parse_error doesn't support multiple lines */
198     if (!result.is_error) {
199         return;
200     }
201
202     fprintf(stream, "%s\n", str);
203     for (size_t i = 0; i < (size_t) (result.end - str); ++i) {
204         fprintf(stream, " ");
205     }
206     fprintf(stream, "^\n");
207     fprintf(stream, "%s\n", result.error_message);
208 }