]> git.lizzy.rs Git - nothing.git/blob - src/script/parser.c
(#299) create_expr_from_str -> parse_expr
[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 bool is_symbol_char(char x)
8 {
9     static const char forbidden_symbol_chars[] = {
10         '(', ')', '"', '\'', ';'
11     };
12     static const size_t n = sizeof(forbidden_symbol_chars) / sizeof(char);
13
14     for (size_t i = 0; i < n; ++i) {
15         if (x == forbidden_symbol_chars[i] || isspace(x)) {
16             return false;
17         }
18     }
19
20     return true;
21 }
22
23 static void skip_whitespaces(const char *str, size_t *cursor, size_t n)
24 {
25     assert(str);
26     assert(cursor);
27
28     while (*cursor < n && isspace(str[*cursor])) {
29         (*cursor)++;
30     }
31 }
32
33 struct ParseResult parse_expr(const char *str,
34                                         size_t *cursor,
35                                         size_t n)
36 {
37     assert(str);
38     assert(cursor);
39
40     /* TODO: parse_expr doesn't parse lists */
41
42     skip_whitespaces(str, cursor, n);
43     if (*cursor >= n) {
44         return parse_failure("EOF", *cursor);
45     }
46
47     switch (str[*cursor]) {
48     case '(': {
49         (*cursor)++;
50         struct ParseResult car = parse_expr(str, cursor, n);
51         if (car.is_error) {
52             return car;
53         }
54
55         skip_whitespaces(str, cursor, n);
56         if (*cursor >= n) {
57             return parse_failure("EOF", *cursor);
58         }
59
60         if (str[*cursor] != '.') {
61             return parse_failure("Expected .", *cursor);
62         }
63         (*cursor)++;
64
65         skip_whitespaces(str, cursor, n);
66         if (*cursor >= n) {
67             return parse_failure("EOF", *cursor);
68         }
69
70         struct ParseResult cdr = parse_expr(str, cursor, n);
71         if (cdr.is_error) {
72             return cdr;
73         }
74
75         skip_whitespaces(str, cursor, n);
76         if (*cursor >= n) {
77             return parse_failure("EOF", *cursor);
78         }
79
80         if (str[*cursor] != ')') {
81             return parse_failure("Expected )", *cursor);
82         }
83
84         (*cursor)++;
85
86         return parse_success(cons_as_expr(create_cons(car.expr, cdr.expr)));
87     }
88
89     case '"': {
90         /* TODO(#292): parser does not support escaped string characters */
91         const size_t str_begin = *cursor + 1;
92         size_t str_end = str_begin;
93
94         while(str_end < n && str[str_end] != '"') {
95             str_end++;
96         }
97
98         if (str_end >= n) {
99             return parse_failure("Unclosed string", str_begin);
100         }
101
102         *cursor = str_end + 1;
103
104         return parse_success(
105             atom_as_expr(
106                 create_string_atom(str + str_begin, str + str_end)));
107     }
108
109     default: {
110         if (isdigit(str[*cursor])) {
111             const char *nptr = str + *cursor;
112             char *endptr = 0;
113             const float x = strtof(nptr, &endptr);
114
115             if (nptr == endptr) {
116                 return parse_failure("Number expected", *cursor);
117             }
118
119             *cursor += (size_t) (endptr - nptr);
120
121             return parse_success(atom_as_expr(create_number_atom(x)));
122         } else if (is_symbol_char(str[*cursor])) {
123             const size_t sym_begin = *cursor;
124             size_t sym_end = sym_begin;
125
126             while (sym_end < n && is_symbol_char(str[sym_end])) {
127                 sym_end++;
128             }
129
130             *cursor = sym_end;
131
132             return parse_success(
133                 atom_as_expr(
134                     create_symbol_atom(str + sym_begin, str + sym_end)));
135         }
136     }
137     }
138
139     return parse_failure("Unexpected sequence of characters", *cursor);
140 }
141
142 struct ParseResult parse_success(struct Expr expr)
143 {
144     struct ParseResult result = {
145         .is_error = false,
146         .expr = expr
147     };
148
149     return result;
150 }
151
152 struct ParseResult parse_failure(const char *error_message,
153                                  size_t error_cursor)
154 {
155     struct ParseResult result = {
156         .is_error = true,
157         .error = {
158             .error_message = error_message,
159             .error_cursor = error_cursor
160         }
161     };
162
163     return result;
164 }
165
166 void print_parse_error(FILE *stream,
167                        const char *str,
168                        struct ParseError error)
169 {
170     /* TODO(#293): print_parse_error doesn't support colors */
171     /* TODO(#294): print_parse_error doesn't support multiple lines */
172
173     fprintf(stream, "%s\n", str);
174     for (size_t i = 0; i < error.error_cursor; ++i) {
175         fprintf(stream, " ");
176     }
177     fprintf(stream, "^\n");
178     fprintf(stream, "%s\n", error.error_message);
179 }