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