]> de.git.xonotic.org Git - xonotic/gmqcc.git/blob - ftepp.c
Basic structure of ftepp
[xonotic/gmqcc.git] / ftepp.c
1 /*
2  * Copyright (C) 2012
3  *     Wolfgang Bumiller
4  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
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
21  * SOFTWARE.
22  */
23 #include "gmqcc.h"
24 #include "lexer.h"
25
26 typedef struct {
27         bool on;
28         bool was_on;
29         bool had_else;
30 } ppcondition;
31
32 typedef struct {
33         lex_file    *lex;
34         int          token;
35         bool         newline;
36         unsigned int errors;
37
38         ppcondition *conditions;
39 } ftepp_t;
40
41 #define ftepp_tokval(f) ((f)->lex->tok.value)
42 #define ftepp_ctx(f)    ((f)->lex->tok.ctx)
43
44 static void ftepp_errorat(ftepp_t *ftepp, lex_ctx ctx, const char *fmt, ...)
45 {
46         va_list ap;
47
48         ftepp->errors++;
49
50         va_start(ap, fmt);
51     con_vprintmsg(LVL_ERROR, ctx.file, ctx.line, "error", fmt, ap);
52         va_end(ap);
53 }
54
55 static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
56 {
57         va_list ap;
58
59         ftepp->errors++;
60
61         va_start(ap, fmt);
62     con_vprintmsg(LVL_ERROR, ftepp->lex->tok.ctx.file, ftepp->lex->tok.ctx.line, "error", fmt, ap);
63         va_end(ap);
64 }
65
66 ftepp_t* ftepp_init()
67 {
68         ftepp_t *ftepp;
69
70         ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
71         memset(ftepp, 0, sizeof(*ftepp));
72
73         return ftepp;
74 }
75
76 static inline int ftepp_next(ftepp_t *ftepp)
77 {
78         return (ftepp->token = lex_do(ftepp->lex));
79 }
80
81 /* Important: this does not skip newlines! */
82 static bool ftepp_skipspace(ftepp_t *ftepp)
83 {
84         while (ftepp_next(ftepp) == TOKEN_WHITE) {}
85         return (ftepp->token < TOKEN_EOF);
86 }
87
88 static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
89 {
90         ftepp_error(ftepp, "TODO: #if");
91         return false;
92 }
93
94 static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
95 {
96         ftepp_error(ftepp, "TODO: #ifdef");
97         return false;
98 }
99
100 static bool ftepp_define(ftepp_t *ftepp)
101 {
102         ftepp_error(ftepp, "TODO: #define");
103         return false;
104 }
105
106 static bool ftepp_else_allowed(ftepp_t *ftepp)
107 {
108         if (!vec_size(ftepp->conditions)) {
109                 ftepp_error(ftepp, "#else without #if");
110                 return false;
111         }
112         if (vec_last(ftepp->conditions).had_else) {
113                 ftepp_error(ftepp, "multiple #else for a single #if");
114                 return false;
115         }
116         return true;
117 }
118
119 static bool ftepp_hash(ftepp_t *ftepp)
120 {
121         ppcondition cond;
122         ppcondition *pc;
123
124         lex_ctx ctx = ftepp_ctx(ftepp);
125
126         if (!ftepp_skipspace(ftepp))
127                 return false;
128
129         switch (ftepp->token) {
130                 case TOKEN_IDENT:
131                         if (!strcmp(ftepp_tokval(ftepp), "define")) {
132                                 return ftepp_define(ftepp);
133                         }
134                         else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
135                                 if (!ftepp_ifdef(ftepp, &cond))
136                                         return false;
137                                 vec_push(ftepp->conditions, cond);
138                                 return true;
139                         }
140                         else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
141                                 if (!ftepp_ifdef(ftepp, &cond))
142                                         return false;
143                                 cond.on = !cond.on;
144                                 vec_push(ftepp->conditions, cond);
145                                 return true;
146                         }
147                         else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
148                                 if (!ftepp_else_allowed(ftepp))
149                                         return false;
150                                 if (!ftepp_ifdef(ftepp, &cond))
151                                         return false;
152                                 pc = &vec_last(ftepp->conditions);
153                                 pc->on     = !pc->was_on && cond.on;
154                                 pc->was_on = pc->was_on || pc->on;
155                                 return true;
156                         }
157                         else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
158                                 if (!ftepp_else_allowed(ftepp))
159                                         return false;
160                                 if (!ftepp_ifdef(ftepp, &cond))
161                                         return false;
162                                 cond.on = !cond.on;
163                                 pc = &vec_last(ftepp->conditions);
164                                 pc->on     = !pc->was_on && cond.on;
165                                 pc->was_on = pc->was_on || pc->on;
166                                 return true;
167                         }
168                         else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
169                                 if (!ftepp_else_allowed(ftepp))
170                                         return false;
171                                 if (!ftepp_if(ftepp, &cond))
172                                         return false;
173                                 pc = &vec_last(ftepp->conditions);
174                                 pc->on     = !pc->was_on && cond.on;
175                                 pc->was_on = pc->was_on  || pc->on;
176                                 return true;
177                         }
178                         else if (!strcmp(ftepp_tokval(ftepp), "if")) {
179                                 if (!ftepp_if(ftepp, &cond))
180                                         return false;
181                                 vec_push(ftepp->conditions, cond);
182                                 return true;
183                         }
184                         else if (!strcmp(ftepp_tokval(ftepp), "else")) {
185                                 if (!ftepp_else_allowed(ftepp))
186                                         return false;
187                                 pc = &vec_last(ftepp->conditions);
188                                 pc->on = !pc->was_on;
189                                 pc->had_else = true;
190                                 return true;
191                         }
192                         else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
193                                 if (!vec_size(ftepp->conditions)) {
194                                         ftepp_error(ftepp, "#endif without #if");
195                                         return false;
196                                 }
197                                 vec_pop(ftepp->conditions);
198                                 break;
199                         }
200                         else {
201                                 ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
202                                 return false;
203                         }
204                         break;
205                 case TOKEN_KEYWORD:
206                         if (!strcmp(ftepp_tokval(ftepp), "if")) {
207                                 if (!ftepp_if(ftepp, &cond))
208                                         return false;
209                                 vec_push(ftepp->conditions, cond);
210                                 return true;
211                         }
212                 /* fall through */
213                 default:
214                         ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
215                         return false;
216                 case TOKEN_EOL:
217                         ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
218                         return false;
219                 case TOKEN_EOF:
220                         ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
221                         return false;
222         }
223         return true;
224 }
225
226 static bool ftepp_preprocess(ftepp_t *ftepp)
227 {
228         bool newline = true;
229
230         ftepp->lex->flags.preprocessing = true;
231
232         do
233         {
234                 ftepp_next(ftepp);
235
236                 if (ftepp->token >= TOKEN_EOF)
237                         break;
238
239                 ftepp->newline = newline;
240                 newline = false;
241
242                 switch (ftepp->token) {
243                         case '#':
244                                 if (!ftepp->newline) {
245                                         printf("%s", ftepp_tokval(ftepp));
246                                         break;
247                                 }
248                                 if (!ftepp_hash(ftepp))
249                                         return false;
250                                 break;
251                         case TOKEN_EOL:
252                                 newline = true;
253                                 printf("\n");
254                                 break;
255                         default:
256                                 printf("%s", ftepp_tokval(ftepp));
257                                 break;
258                 }
259         } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
260
261         return (ftepp->token == TOKEN_EOF);
262 }
263
264 bool ftepp_preprocess_file(const char *filename)
265 {
266         ftepp_t *ftepp = ftepp_init();
267     ftepp->lex = lex_open(filename);
268     if (!ftepp->lex) {
269         con_out("failed to open file \"%s\"\n", filename);
270         return false;
271     }
272     return ftepp_preprocess(ftepp);
273 }
274
275 bool ftepp_preprocess_string(const char *name, const char *str)
276 {
277         ftepp_t *ftepp = ftepp_init();
278     ftepp->lex = lex_open_string(str, strlen(str), name);
279     if (!ftepp->lex) {
280         con_out("failed to create lexer for string \"%s\"\n", name);
281         return false;
282     }
283     return ftepp_preprocess(ftepp);
284 }