6 #include "script/parser.h"
8 static struct ParseResult parse_expr(struct Token current_token);
10 static struct ParseResult parse_cdr(struct Token current_token)
12 if (*current_token.begin != '.') {
13 return parse_failure("Expected .", current_token.begin);
16 struct ParseResult cdr = read_expr_from_string(current_token.end);
21 current_token = next_token(cdr.end);
23 if (*current_token.begin != ')') {
24 destroy_expr(cdr.expr);
25 return parse_failure("Expected )", current_token.begin);
28 return parse_success(cdr.expr, current_token.end);
31 static struct ParseResult parse_list_end(struct Token current_token)
33 if (*current_token.begin != ')') {
34 return parse_failure("Expected )", current_token.begin);
37 return parse_success(atom_as_expr(create_symbol_atom("nil", NULL)),
41 static struct ParseResult parse_list(struct Token current_token)
43 if (*current_token.begin != '(') {
44 return parse_failure("Expected (", current_token.begin);
47 current_token = next_token(current_token.end);
49 if (*current_token.begin == ')') {
50 return parse_list_end(current_token);
53 struct ParseResult car = parse_expr(current_token);
58 struct Cons *list = create_cons(car.expr, void_expr());
59 struct Cons *cons = list;
60 current_token = next_token(car.end);
62 while (*current_token.begin != '.' &&
63 *current_token.begin != ')' &&
64 *current_token.begin != 0) {
65 car = parse_expr(current_token);
71 cons->cdr = cons_as_expr(create_cons(car.expr, void_expr()));
72 cons = cons->cdr.cons;
74 current_token = next_token(car.end);
77 struct ParseResult cdr = *current_token.begin == '.'
78 ? parse_cdr(current_token)
79 : parse_list_end(current_token);
88 return parse_success(cons_as_expr(list), cdr.end);
91 static struct ParseResult parse_string(struct Token current_token)
93 if (*current_token.begin != '"') {
94 return parse_failure("Expected \"", current_token.begin);
97 if (*(current_token.end - 1) != '"') {
98 return parse_failure("Unclosed string", current_token.begin);
101 if (current_token.begin + 1 == current_token.end) {
102 return parse_success(atom_as_expr(create_string_atom("", NULL)),
106 return parse_success(
108 create_string_atom(current_token.begin + 1, current_token.end - 1)),
112 static struct ParseResult parse_number(struct Token current_token)
115 const float x = strtof(current_token.begin, &endptr);
117 if (current_token.begin == endptr || current_token.end != endptr) {
118 return parse_failure("Expected number", current_token.begin);
121 return parse_success(
122 atom_as_expr(create_number_atom(x)),
126 static struct ParseResult parse_symbol(struct Token current_token)
128 if (*current_token.begin == 0) {
129 return parse_failure("EOF", current_token.begin);
132 return parse_success(
133 atom_as_expr(create_symbol_atom(current_token.begin, current_token.end)),
137 static struct ParseResult parse_expr(struct Token current_token)
139 if (*current_token.begin == 0) {
140 return parse_failure("EOF", current_token.begin);
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);
150 if (isdigit(*current_token.begin)) {
151 return parse_number(current_token);
154 return parse_symbol(current_token);
157 struct ParseResult read_expr_from_string(const char *str)
160 return parse_expr(next_token(str));
163 struct ParseResult read_expr_from_stream(FILE *stream)
166 return parse_failure("not implemented", NULL);
169 struct ParseResult parse_success(struct Expr expr,
172 struct ParseResult result = {
181 struct ParseResult parse_failure(const char *error_message,
184 struct ParseResult result = {
186 .error_message = error_message,
193 void print_parse_error(FILE *stream,
195 struct ParseResult result)
197 /* TODO(#294): print_parse_error doesn't support multiple lines */
198 if (!result.is_error) {
202 fprintf(stream, "%s\n", str);
203 for (size_t i = 0; i < (size_t) (result.end - str); ++i) {
204 fprintf(stream, " ");
206 fprintf(stream, "^\n");
207 fprintf(stream, "%s\n", result.error_message);