5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is furnished to do
10 * so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38 ppcondition *conditions;
41 #define ftepp_tokval(f) ((f)->lex->tok.value)
42 #define ftepp_ctx(f) ((f)->lex->tok.ctx)
44 static void ftepp_errorat(ftepp_t *ftepp, lex_ctx ctx, const char *fmt, ...)
51 con_vprintmsg(LVL_ERROR, ctx.file, ctx.line, "error", fmt, ap);
55 static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
62 con_vprintmsg(LVL_ERROR, ftepp->lex->tok.ctx.file, ftepp->lex->tok.ctx.line, "error", fmt, ap);
70 ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
71 memset(ftepp, 0, sizeof(*ftepp));
76 static inline int ftepp_next(ftepp_t *ftepp)
78 return (ftepp->token = lex_do(ftepp->lex));
81 /* Important: this does not skip newlines! */
82 static bool ftepp_skipspace(ftepp_t *ftepp)
84 while (ftepp_next(ftepp) == TOKEN_WHITE) {}
85 return (ftepp->token < TOKEN_EOF);
88 static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
90 ftepp_error(ftepp, "TODO: #if");
94 static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
96 ftepp_error(ftepp, "TODO: #ifdef");
100 static bool ftepp_define(ftepp_t *ftepp)
102 ftepp_error(ftepp, "TODO: #define");
106 static bool ftepp_else_allowed(ftepp_t *ftepp)
108 if (!vec_size(ftepp->conditions)) {
109 ftepp_error(ftepp, "#else without #if");
112 if (vec_last(ftepp->conditions).had_else) {
113 ftepp_error(ftepp, "multiple #else for a single #if");
119 static bool ftepp_hash(ftepp_t *ftepp)
124 lex_ctx ctx = ftepp_ctx(ftepp);
126 if (!ftepp_skipspace(ftepp))
129 switch (ftepp->token) {
131 if (!strcmp(ftepp_tokval(ftepp), "define")) {
132 return ftepp_define(ftepp);
134 else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
135 if (!ftepp_ifdef(ftepp, &cond))
137 vec_push(ftepp->conditions, cond);
140 else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
141 if (!ftepp_ifdef(ftepp, &cond))
144 vec_push(ftepp->conditions, cond);
147 else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
148 if (!ftepp_else_allowed(ftepp))
150 if (!ftepp_ifdef(ftepp, &cond))
152 pc = &vec_last(ftepp->conditions);
153 pc->on = !pc->was_on && cond.on;
154 pc->was_on = pc->was_on || pc->on;
157 else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
158 if (!ftepp_else_allowed(ftepp))
160 if (!ftepp_ifdef(ftepp, &cond))
163 pc = &vec_last(ftepp->conditions);
164 pc->on = !pc->was_on && cond.on;
165 pc->was_on = pc->was_on || pc->on;
168 else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
169 if (!ftepp_else_allowed(ftepp))
171 if (!ftepp_if(ftepp, &cond))
173 pc = &vec_last(ftepp->conditions);
174 pc->on = !pc->was_on && cond.on;
175 pc->was_on = pc->was_on || pc->on;
178 else if (!strcmp(ftepp_tokval(ftepp), "if")) {
179 if (!ftepp_if(ftepp, &cond))
181 vec_push(ftepp->conditions, cond);
184 else if (!strcmp(ftepp_tokval(ftepp), "else")) {
185 if (!ftepp_else_allowed(ftepp))
187 pc = &vec_last(ftepp->conditions);
188 pc->on = !pc->was_on;
192 else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
193 if (!vec_size(ftepp->conditions)) {
194 ftepp_error(ftepp, "#endif without #if");
197 vec_pop(ftepp->conditions);
201 ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
206 if (!strcmp(ftepp_tokval(ftepp), "if")) {
207 if (!ftepp_if(ftepp, &cond))
209 vec_push(ftepp->conditions, cond);
214 ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
217 ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
220 ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
226 static bool ftepp_preprocess(ftepp_t *ftepp)
230 ftepp->lex->flags.preprocessing = true;
236 if (ftepp->token >= TOKEN_EOF)
239 ftepp->newline = newline;
242 switch (ftepp->token) {
244 if (!ftepp->newline) {
245 printf("%s", ftepp_tokval(ftepp));
248 if (!ftepp_hash(ftepp))
256 printf("%s", ftepp_tokval(ftepp));
259 } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
261 return (ftepp->token == TOKEN_EOF);
264 bool ftepp_preprocess_file(const char *filename)
266 ftepp_t *ftepp = ftepp_init();
267 ftepp->lex = lex_open(filename);
269 con_out("failed to open file \"%s\"\n", filename);
272 return ftepp_preprocess(ftepp);
275 bool ftepp_preprocess_string(const char *name, const char *str)
277 ftepp_t *ftepp = ftepp_init();
278 ftepp->lex = lex_open_string(str, strlen(str), name);
280 con_out("failed to create lexer for string \"%s\"\n", name);
283 return ftepp_preprocess(ftepp);