Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6429 | siemargl | 1 | /* |
2 | * TCC - Tiny C Compiler |
||
3 | * |
||
4 | * Copyright (c) 2001-2004 Fabrice Bellard |
||
5 | * |
||
6 | * This library is free software; you can redistribute it and/or |
||
7 | * modify it under the terms of the GNU Lesser General Public |
||
8 | * License as published by the Free Software Foundation; either |
||
9 | * version 2 of the License, or (at your option) any later version. |
||
10 | * |
||
11 | * This library is distributed in the hope that it will be useful, |
||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
14 | * Lesser General Public License for more details. |
||
15 | * |
||
16 | * You should have received a copy of the GNU Lesser General Public |
||
17 | * License along with this library; if not, write to the Free Software |
||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
19 | */ |
||
20 | |||
21 | #include "tcc.h" |
||
22 | |||
23 | /********************************************************/ |
||
24 | /* global variables */ |
||
25 | |||
26 | ST_DATA int tok_flags; |
||
27 | ST_DATA int parse_flags; |
||
28 | |||
29 | ST_DATA struct BufferedFile *file; |
||
30 | ST_DATA int ch, tok; |
||
31 | ST_DATA CValue tokc; |
||
32 | ST_DATA const int *macro_ptr; |
||
33 | ST_DATA CString tokcstr; /* current parsed string, if any */ |
||
34 | |||
35 | /* display benchmark infos */ |
||
36 | ST_DATA int total_lines; |
||
37 | ST_DATA int total_bytes; |
||
38 | ST_DATA int tok_ident; |
||
39 | ST_DATA TokenSym **table_ident; |
||
40 | |||
41 | ST_DATA TinyAlloc *toksym_alloc; |
||
42 | ST_DATA TinyAlloc *tokstr_alloc; |
||
43 | ST_DATA TinyAlloc *cstr_alloc; |
||
44 | |||
45 | /* ------------------------------------------------------------------------- */ |
||
46 | |||
47 | static TokenSym *hash_ident[TOK_HASH_SIZE]; |
||
48 | static char token_buf[STRING_MAX_SIZE + 1]; |
||
49 | static CString cstr_buf; |
||
50 | static TokenString tokstr_buf; |
||
51 | static unsigned char isidnum_table[256 - CH_EOF]; |
||
52 | /* isidnum_table flags: */ |
||
53 | #define IS_SPC 1 |
||
54 | #define IS_ID 2 |
||
55 | #define IS_NUM 4 |
||
56 | |||
57 | static TokenString *macro_stack; |
||
58 | |||
59 | static const char tcc_keywords[] = |
||
60 | #define DEF(id, str) str "\0" |
||
61 | #include "tcctok.h" |
||
62 | #undef DEF |
||
63 | ; |
||
64 | |||
65 | /* WARNING: the content of this string encodes token numbers */ |
||
66 | static const unsigned char tok_two_chars[] = |
||
67 | /* outdated -- gr |
||
68 | "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253" |
||
69 | "-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266"; |
||
70 | */{ |
||
71 | '<','=', TOK_LE, |
||
72 | '>','=', TOK_GE, |
||
73 | '!','=', TOK_NE, |
||
74 | '&','&', TOK_LAND, |
||
75 | '|','|', TOK_LOR, |
||
76 | '+','+', TOK_INC, |
||
77 | '-','-', TOK_DEC, |
||
78 | '=','=', TOK_EQ, |
||
79 | '<','<', TOK_SHL, |
||
80 | '>','>', TOK_SAR, |
||
81 | '+','=', TOK_A_ADD, |
||
82 | '-','=', TOK_A_SUB, |
||
83 | '*','=', TOK_A_MUL, |
||
84 | '/','=', TOK_A_DIV, |
||
85 | '%','=', TOK_A_MOD, |
||
86 | '&','=', TOK_A_AND, |
||
87 | '^','=', TOK_A_XOR, |
||
88 | '|','=', TOK_A_OR, |
||
89 | '-','>', TOK_ARROW, |
||
90 | '.','.', 0xa8, // C++ token ? |
||
91 | '#','#', TOK_TWOSHARPS, |
||
92 | |||
93 | }; |
||
94 | |||
95 | static void next_nomacro_spc(void); |
||
96 | |||
97 | ST_FUNC void skip(int c) |
||
98 | { |
||
99 | if (tok != c) |
||
100 | tcc_error("'%c' expected (got \"%s\")", c, get_tok_str(tok, &tokc)); |
||
101 | next(); |
||
102 | } |
||
103 | |||
104 | ST_FUNC void expect(const char *msg) |
||
105 | { |
||
106 | tcc_error("%s expected", msg); |
||
107 | } |
||
108 | |||
109 | ST_FUNC void begin_macro(TokenString *str, int alloc) |
||
110 | { |
||
111 | str->alloc = alloc; |
||
112 | str->prev = macro_stack; |
||
113 | str->prev_ptr = macro_ptr; |
||
114 | macro_ptr = str->str; |
||
115 | macro_stack = str; |
||
116 | } |
||
117 | |||
118 | ST_FUNC void end_macro(void) |
||
119 | { |
||
120 | TokenString *str = macro_stack; |
||
121 | macro_stack = str->prev; |
||
122 | macro_ptr = str->prev_ptr; |
||
123 | if (str->alloc == 2) { |
||
124 | str->alloc = 3; /* just mark as finished */ |
||
125 | } else { |
||
126 | tok_str_free(str->str); |
||
127 | if (str->alloc == 1) |
||
128 | tcc_free(str); |
||
129 | } |
||
130 | } |
||
131 | |||
132 | /* ------------------------------------------------------------------------- */ |
||
133 | /* Custom allocator for tiny objects */ |
||
134 | |||
135 | ST_FUNC TinyAlloc *tal_new(TinyAlloc **pal, size_t limit, size_t size) |
||
136 | { |
||
137 | TinyAlloc *al = tcc_mallocz(sizeof(TinyAlloc)); |
||
138 | al->p = al->buffer = tcc_malloc(size); |
||
139 | al->limit = limit; |
||
140 | al->size = size; |
||
141 | if (pal) *pal = al; |
||
142 | return al; |
||
143 | } |
||
144 | |||
145 | ST_FUNC void tal_delete(TinyAlloc *al) |
||
146 | { |
||
147 | TinyAlloc *next; |
||
148 | |||
149 | tail_call: |
||
150 | if (!al) |
||
151 | return; |
||
152 | #ifdef TAL_INFO |
||
153 | fprintf(stderr, "limit=%5d, size=%5g MB, nb_peak=%6d, nb_total=%8d, nb_missed=%6d, usage=%5.1f%%\n", |
||
154 | al->limit, al->size / 1024.0 / 1024.0, al->nb_peak, al->nb_total, al->nb_missed, |
||
155 | (al->peak_p - al->buffer) * 100.0 / al->size); |
||
156 | #endif |
||
157 | #ifdef TAL_DEBUG |
||
158 | if (al->nb_allocs > 0) { |
||
159 | fprintf(stderr, "TAL_DEBUG: mem leak %d chunks (limit= %d)\n", |
||
160 | al->nb_allocs, al->limit); |
||
161 | uint8_t *p = al->buffer; |
||
162 | while (p < al->p) { |
||
163 | tal_header_t *header = (tal_header_t *)p; |
||
164 | if (header->line_num > 0) { |
||
165 | fprintf(stderr, " file %s, line %u: %u bytes\n", |
||
166 | header->file_name, header->line_num, header->size); |
||
167 | } |
||
168 | p += header->size + sizeof(tal_header_t); |
||
169 | } |
||
170 | } |
||
171 | #endif |
||
172 | next = al->next; |
||
173 | tcc_free(al->buffer); |
||
174 | tcc_free(al); |
||
175 | al = next; |
||
176 | goto tail_call; |
||
177 | } |
||
178 | |||
179 | ST_FUNC void tal_free_impl(TinyAlloc *al, void *p TAL_DEBUG_PARAMS) |
||
180 | { |
||
181 | if (!p) |
||
182 | return; |
||
183 | tail_call: |
||
184 | if (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size) { |
||
185 | #ifdef TAL_DEBUG |
||
186 | tal_header_t *header = (((tal_header_t *)p) - 1); |
||
187 | if (header->line_num < 0) { |
||
188 | fprintf(stderr, "TAL_DEBUG: file %s, line %u double frees chunk from\n", |
||
189 | file, line); |
||
190 | fprintf(stderr, " file %s, line %u: %u bytes\n", |
||
191 | header->file_name, -header->line_num, header->size); |
||
192 | } else |
||
193 | header->line_num = -header->line_num; |
||
194 | #endif |
||
195 | al->nb_allocs--; |
||
196 | if (!al->nb_allocs) |
||
197 | al->p = al->buffer; |
||
198 | } else if (al->next) { |
||
199 | al = al->next; |
||
200 | goto tail_call; |
||
201 | } |
||
202 | else |
||
203 | tcc_free(p); |
||
204 | } |
||
205 | |||
206 | ST_FUNC void *tal_realloc_impl(TinyAlloc **pal, void *p, size_t size TAL_DEBUG_PARAMS) |
||
207 | { |
||
208 | tal_header_t *header; |
||
209 | void *ret; |
||
210 | int is_own; |
||
211 | size_t adj_size = (size + 3) & -4; |
||
212 | TinyAlloc *al = *pal; |
||
213 | |||
214 | tail_call: |
||
215 | is_own = (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size); |
||
216 | if ((!p || is_own) && size <= al->limit) { |
||
217 | if (al->p + adj_size + sizeof(tal_header_t) < al->buffer + al->size) { |
||
218 | header = (tal_header_t *)al->p; |
||
219 | header->size = adj_size; |
||
220 | #ifdef TAL_DEBUG |
||
221 | int ofs = strlen(file) - TAL_DEBUG_FILE_LEN; |
||
222 | strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), TAL_DEBUG_FILE_LEN); |
||
223 | header->file_name[TAL_DEBUG_FILE_LEN] = 0; |
||
224 | header->line_num = line; |
||
225 | #endif |
||
226 | ret = al->p + sizeof(tal_header_t); |
||
227 | al->p += adj_size + sizeof(tal_header_t); |
||
228 | if (is_own) { |
||
229 | header = (((tal_header_t *)p) - 1); |
||
230 | memcpy(ret, p, header->size); |
||
231 | #ifdef TAL_DEBUG |
||
232 | header->line_num = -header->line_num; |
||
233 | #endif |
||
234 | } else { |
||
235 | al->nb_allocs++; |
||
236 | } |
||
237 | #ifdef TAL_INFO |
||
238 | if (al->nb_peak < al->nb_allocs) |
||
239 | al->nb_peak = al->nb_allocs; |
||
240 | if (al->peak_p < al->p) |
||
241 | al->peak_p = al->p; |
||
242 | al->nb_total++; |
||
243 | #endif |
||
244 | return ret; |
||
245 | } else if (is_own) { |
||
246 | al->nb_allocs--; |
||
247 | ret = tal_realloc(*pal, 0, size); |
||
248 | header = (((tal_header_t *)p) - 1); |
||
249 | memcpy(ret, p, header->size); |
||
250 | #ifdef TAL_DEBUG |
||
251 | header->line_num = -header->line_num; |
||
252 | #endif |
||
253 | return ret; |
||
254 | } |
||
255 | if (al->next) { |
||
256 | al = al->next; |
||
257 | } else { |
||
258 | TinyAlloc *bottom = al, *next = al->top ? al->top : al; |
||
259 | |||
260 | al = tal_new(pal, next->limit, next->size * 2); |
||
261 | al->next = next; |
||
262 | bottom->top = al; |
||
263 | } |
||
264 | goto tail_call; |
||
265 | } |
||
266 | if (is_own) { |
||
267 | al->nb_allocs--; |
||
268 | ret = tcc_malloc(size); |
||
269 | header = (((tal_header_t *)p) - 1); |
||
270 | memcpy(ret, p, header->size); |
||
271 | #ifdef TAL_DEBUG |
||
272 | header->line_num = -header->line_num; |
||
273 | #endif |
||
274 | } else if (al->next) { |
||
275 | al = al->next; |
||
276 | goto tail_call; |
||
277 | } else |
||
278 | ret = tcc_realloc(p, size); |
||
279 | #ifdef TAL_INFO |
||
280 | al->nb_missed++; |
||
281 | #endif |
||
282 | return ret; |
||
283 | } |
||
284 | |||
285 | /* ------------------------------------------------------------------------- */ |
||
286 | /* CString handling */ |
||
287 | static void cstr_realloc(CString *cstr, int new_size) |
||
288 | { |
||
289 | int size; |
||
290 | void *data; |
||
291 | |||
292 | size = cstr->size_allocated; |
||
293 | if (size < 8) |
||
294 | size = 8; /* no need to allocate a too small first string */ |
||
295 | while (size < new_size) |
||
296 | size = size * 2; |
||
297 | data = tal_realloc(cstr_alloc, cstr->data_allocated, size); |
||
298 | cstr->data_allocated = data; |
||
299 | cstr->size_allocated = size; |
||
300 | cstr->data = data; |
||
301 | } |
||
302 | |||
303 | /* add a byte */ |
||
304 | ST_FUNC void cstr_ccat(CString *cstr, int ch) |
||
305 | { |
||
306 | int size; |
||
307 | size = cstr->size + 1; |
||
308 | if (size > cstr->size_allocated) |
||
309 | cstr_realloc(cstr, size); |
||
310 | ((unsigned char *)cstr->data)[size - 1] = ch; |
||
311 | cstr->size = size; |
||
312 | } |
||
313 | |||
314 | ST_FUNC void cstr_cat(CString *cstr, const char *str, int len) |
||
315 | { |
||
316 | int size; |
||
317 | if (len <= 0) |
||
318 | len = strlen(str) + 1 + len; |
||
319 | size = cstr->size + len; |
||
320 | if (size > cstr->size_allocated) |
||
321 | cstr_realloc(cstr, size); |
||
322 | memmove(((unsigned char *)cstr->data) + cstr->size, str, len); |
||
323 | cstr->size = size; |
||
324 | } |
||
325 | |||
326 | /* add a wide char */ |
||
327 | ST_FUNC void cstr_wccat(CString *cstr, int ch) |
||
328 | { |
||
329 | int size; |
||
330 | size = cstr->size + sizeof(nwchar_t); |
||
331 | if (size > cstr->size_allocated) |
||
332 | cstr_realloc(cstr, size); |
||
333 | *(nwchar_t *)(((unsigned char *)cstr->data) + size - sizeof(nwchar_t)) = ch; |
||
334 | cstr->size = size; |
||
335 | } |
||
336 | |||
337 | ST_FUNC void cstr_new(CString *cstr) |
||
338 | { |
||
339 | memset(cstr, 0, sizeof(CString)); |
||
340 | } |
||
341 | |||
342 | /* free string and reset it to NULL */ |
||
343 | ST_FUNC void cstr_free(CString *cstr) |
||
344 | { |
||
345 | tal_free(cstr_alloc, cstr->data_allocated); |
||
346 | cstr_new(cstr); |
||
347 | } |
||
348 | |||
349 | /* reset string to empty */ |
||
350 | ST_FUNC void cstr_reset(CString *cstr) |
||
351 | { |
||
352 | cstr->size = 0; |
||
353 | } |
||
354 | |||
355 | /* XXX: unicode ? */ |
||
356 | static void add_char(CString *cstr, int c) |
||
357 | { |
||
358 | if (c == '\'' || c == '\"' || c == '\\') { |
||
359 | /* XXX: could be more precise if char or string */ |
||
360 | cstr_ccat(cstr, '\\'); |
||
361 | } |
||
362 | if (c >= 32 && c <= 126) { |
||
363 | cstr_ccat(cstr, c); |
||
364 | } else { |
||
365 | cstr_ccat(cstr, '\\'); |
||
366 | if (c == '\n') { |
||
367 | cstr_ccat(cstr, 'n'); |
||
368 | } else { |
||
369 | cstr_ccat(cstr, '0' + ((c >> 6) & 7)); |
||
370 | cstr_ccat(cstr, '0' + ((c >> 3) & 7)); |
||
371 | cstr_ccat(cstr, '0' + (c & 7)); |
||
372 | } |
||
373 | } |
||
374 | } |
||
375 | |||
376 | /* ------------------------------------------------------------------------- */ |
||
377 | /* allocate a new token */ |
||
378 | static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len) |
||
379 | { |
||
380 | TokenSym *ts, **ptable; |
||
381 | int i; |
||
382 | |||
383 | if (tok_ident >= SYM_FIRST_ANOM) |
||
384 | tcc_error("memory full (symbols)"); |
||
385 | |||
386 | /* expand token table if needed */ |
||
387 | i = tok_ident - TOK_IDENT; |
||
388 | if ((i % TOK_ALLOC_INCR) == 0) { |
||
389 | ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *)); |
||
390 | table_ident = ptable; |
||
391 | } |
||
392 | |||
393 | ts = tal_realloc(toksym_alloc, 0, sizeof(TokenSym) + len); |
||
394 | table_ident[i] = ts; |
||
395 | ts->tok = tok_ident++; |
||
396 | ts->sym_define = NULL; |
||
397 | ts->sym_label = NULL; |
||
398 | ts->sym_struct = NULL; |
||
399 | ts->sym_identifier = NULL; |
||
400 | ts->len = len; |
||
401 | ts->hash_next = NULL; |
||
402 | memcpy(ts->str, str, len); |
||
403 | ts->str[len] = '\0'; |
||
404 | *pts = ts; |
||
405 | return ts; |
||
406 | } |
||
407 | |||
408 | #define TOK_HASH_INIT 1 |
||
409 | #define TOK_HASH_FUNC(h, c) ((h) + ((h) << 5) + ((h) >> 27) + (c)) |
||
410 | |||
411 | |||
412 | /* find a token and add it if not found */ |
||
413 | ST_FUNC TokenSym *tok_alloc(const char *str, int len) |
||
414 | { |
||
415 | TokenSym *ts, **pts; |
||
416 | int i; |
||
417 | unsigned int h; |
||
418 | |||
419 | h = TOK_HASH_INIT; |
||
420 | for(i=0;i |
||
421 | h = TOK_HASH_FUNC(h, ((unsigned char *)str)[i]); |
||
422 | h &= (TOK_HASH_SIZE - 1); |
||
423 | |||
424 | pts = &hash_ident[h]; |
||
425 | for(;;) { |
||
426 | ts = *pts; |
||
427 | if (!ts) |
||
428 | break; |
||
429 | if (ts->len == len && !memcmp(ts->str, str, len)) |
||
430 | return ts; |
||
431 | pts = &(ts->hash_next); |
||
432 | } |
||
433 | return tok_alloc_new(pts, str, len); |
||
434 | } |
||
435 | |||
436 | /* XXX: buffer overflow */ |
||
437 | /* XXX: float tokens */ |
||
438 | ST_FUNC const char *get_tok_str(int v, CValue *cv) |
||
439 | { |
||
440 | char *p; |
||
441 | int i, len; |
||
442 | |||
443 | cstr_reset(&cstr_buf); |
||
444 | p = cstr_buf.data; |
||
445 | |||
446 | switch(v) { |
||
447 | case TOK_CINT: |
||
448 | case TOK_CUINT: |
||
449 | /* XXX: not quite exact, but only useful for testing */ |
||
450 | sprintf(p, "%llu", (unsigned long long)cv->i); |
||
451 | break; |
||
452 | case TOK_CLLONG: |
||
453 | case TOK_CULLONG: |
||
454 | /* XXX: not quite exact, but only useful for testing */ |
||
455 | #ifdef _WIN32 |
||
456 | sprintf(p, "%u", (unsigned)cv->i); |
||
457 | #else |
||
458 | sprintf(p, "%llu", (unsigned long long)cv->i); |
||
459 | #endif |
||
460 | break; |
||
461 | case TOK_LCHAR: |
||
462 | cstr_ccat(&cstr_buf, 'L'); |
||
463 | case TOK_CCHAR: |
||
464 | cstr_ccat(&cstr_buf, '\''); |
||
465 | add_char(&cstr_buf, cv->i); |
||
466 | cstr_ccat(&cstr_buf, '\''); |
||
467 | cstr_ccat(&cstr_buf, '\0'); |
||
468 | break; |
||
469 | case TOK_PPNUM: |
||
470 | case TOK_PPSTR: |
||
471 | return (char*)cv->str.data; |
||
472 | case TOK_LSTR: |
||
473 | cstr_ccat(&cstr_buf, 'L'); |
||
474 | case TOK_STR: |
||
475 | cstr_ccat(&cstr_buf, '\"'); |
||
476 | if (v == TOK_STR) { |
||
477 | len = cv->str.size - 1; |
||
478 | for(i=0;i |
||
479 | add_char(&cstr_buf, ((unsigned char *)cv->str.data)[i]); |
||
480 | } else { |
||
481 | len = (cv->str.size / sizeof(nwchar_t)) - 1; |
||
482 | for(i=0;i |
||
483 | add_char(&cstr_buf, ((nwchar_t *)cv->str.data)[i]); |
||
484 | } |
||
485 | cstr_ccat(&cstr_buf, '\"'); |
||
486 | cstr_ccat(&cstr_buf, '\0'); |
||
487 | break; |
||
488 | |||
489 | case TOK_CFLOAT: |
||
490 | cstr_cat(&cstr_buf, " |
||
491 | break; |
||
492 | case TOK_CDOUBLE: |
||
493 | cstr_cat(&cstr_buf, " |
||
494 | break; |
||
495 | case TOK_CLDOUBLE: |
||
496 | cstr_cat(&cstr_buf, " |
||
497 | break; |
||
498 | case TOK_LINENUM: |
||
499 | cstr_cat(&cstr_buf, " |
||
500 | break; |
||
501 | |||
502 | /* above tokens have value, the ones below don't */ |
||
503 | |||
504 | case TOK_LT: |
||
505 | v = '<'; |
||
506 | goto addv; |
||
507 | case TOK_GT: |
||
508 | v = '>'; |
||
509 | goto addv; |
||
510 | case TOK_DOTS: |
||
511 | return strcpy(p, "..."); |
||
512 | case TOK_A_SHL: |
||
513 | return strcpy(p, "<<="); |
||
514 | case TOK_A_SAR: |
||
515 | return strcpy(p, ">>="); |
||
516 | default: |
||
517 | if (v < TOK_IDENT) { |
||
518 | /* search in two bytes table */ |
||
519 | const unsigned char *q = tok_two_chars; |
||
520 | while (*q) { |
||
521 | if (q[2] == v) { |
||
522 | *p++ = q[0]; |
||
523 | *p++ = q[1]; |
||
524 | *p = '\0'; |
||
525 | return cstr_buf.data; |
||
526 | } |
||
527 | q += 3; |
||
528 | } |
||
529 | if (v >= 127) { |
||
530 | sprintf(cstr_buf.data, "<%02x>", v); |
||
531 | return cstr_buf.data; |
||
532 | } |
||
533 | addv: |
||
534 | *p++ = v; |
||
535 | *p = '\0'; |
||
536 | } else if (v < tok_ident) { |
||
537 | return table_ident[v - TOK_IDENT]->str; |
||
538 | } else if (v >= SYM_FIRST_ANOM) { |
||
539 | /* special name for anonymous symbol */ |
||
540 | sprintf(p, "L.%u", v - SYM_FIRST_ANOM); |
||
541 | } else { |
||
542 | /* should never happen */ |
||
543 | return NULL; |
||
544 | } |
||
545 | break; |
||
546 | } |
||
547 | return cstr_buf.data; |
||
548 | } |
||
549 | |||
550 | /* return the current character, handling end of block if necessary |
||
551 | (but not stray) */ |
||
552 | ST_FUNC int handle_eob(void) |
||
553 | { |
||
554 | BufferedFile *bf = file; |
||
555 | int len; |
||
556 | |||
557 | /* only tries to read if really end of buffer */ |
||
558 | if (bf->buf_ptr >= bf->buf_end) { |
||
559 | if (bf->fd != -1) { |
||
560 | #if defined(PARSE_DEBUG) |
||
561 | len = 1; |
||
562 | #else |
||
563 | len = IO_BUF_SIZE; |
||
564 | #endif |
||
565 | len = read(bf->fd, bf->buffer, len); |
||
566 | if (len < 0) |
||
567 | len = 0; |
||
568 | } else { |
||
569 | len = 0; |
||
570 | } |
||
571 | total_bytes += len; |
||
572 | bf->buf_ptr = bf->buffer; |
||
573 | bf->buf_end = bf->buffer + len; |
||
574 | *bf->buf_end = CH_EOB; |
||
575 | } |
||
576 | if (bf->buf_ptr < bf->buf_end) { |
||
577 | return bf->buf_ptr[0]; |
||
578 | } else { |
||
579 | bf->buf_ptr = bf->buf_end; |
||
580 | return CH_EOF; |
||
581 | } |
||
582 | } |
||
583 | |||
584 | /* read next char from current input file and handle end of input buffer */ |
||
585 | ST_INLN void inp(void) |
||
586 | { |
||
587 | ch = *(++(file->buf_ptr)); |
||
588 | /* end of buffer/file handling */ |
||
589 | if (ch == CH_EOB) |
||
590 | ch = handle_eob(); |
||
591 | } |
||
592 | |||
593 | /* handle '\[\r]\n' */ |
||
594 | static int handle_stray_noerror(void) |
||
595 | { |
||
596 | while (ch == '\\') { |
||
597 | inp(); |
||
598 | if (ch == '\n') { |
||
599 | file->line_num++; |
||
600 | inp(); |
||
601 | } else if (ch == '\r') { |
||
602 | inp(); |
||
603 | if (ch != '\n') |
||
604 | goto fail; |
||
605 | file->line_num++; |
||
606 | inp(); |
||
607 | } else { |
||
608 | fail: |
||
609 | return 1; |
||
610 | } |
||
611 | } |
||
612 | return 0; |
||
613 | } |
||
614 | |||
615 | static void handle_stray(void) |
||
616 | { |
||
617 | if (handle_stray_noerror()) |
||
618 | tcc_error("stray '\\' in program"); |
||
619 | } |
||
620 | |||
621 | /* skip the stray and handle the \\n case. Output an error if |
||
622 | incorrect char after the stray */ |
||
623 | static int handle_stray1(uint8_t *p) |
||
624 | { |
||
625 | int c; |
||
626 | |||
627 | file->buf_ptr = p; |
||
628 | if (p >= file->buf_end) { |
||
629 | c = handle_eob(); |
||
630 | if (c != '\\') |
||
631 | return c; |
||
632 | p = file->buf_ptr; |
||
633 | } |
||
634 | ch = *p; |
||
635 | if (handle_stray_noerror()) { |
||
636 | if (!(parse_flags & PARSE_FLAG_ACCEPT_STRAYS)) |
||
637 | tcc_error("stray '\\' in program"); |
||
638 | *--file->buf_ptr = '\\'; |
||
639 | } |
||
640 | p = file->buf_ptr; |
||
641 | c = *p; |
||
642 | return c; |
||
643 | } |
||
644 | |||
645 | /* handle just the EOB case, but not stray */ |
||
646 | #define PEEKC_EOB(c, p)\ |
||
647 | {\ |
||
648 | p++;\ |
||
649 | c = *p;\ |
||
650 | if (c == '\\') {\ |
||
651 | file->buf_ptr = p;\ |
||
652 | c = handle_eob();\ |
||
653 | p = file->buf_ptr;\ |
||
654 | }\ |
||
655 | } |
||
656 | |||
657 | /* handle the complicated stray case */ |
||
658 | #define PEEKC(c, p)\ |
||
659 | {\ |
||
660 | p++;\ |
||
661 | c = *p;\ |
||
662 | if (c == '\\') {\ |
||
663 | c = handle_stray1(p);\ |
||
664 | p = file->buf_ptr;\ |
||
665 | }\ |
||
666 | } |
||
667 | |||
668 | /* input with '\[\r]\n' handling. Note that this function cannot |
||
669 | handle other characters after '\', so you cannot call it inside |
||
670 | strings or comments */ |
||
671 | ST_FUNC void minp(void) |
||
672 | { |
||
673 | inp(); |
||
674 | if (ch == '\\') |
||
675 | handle_stray(); |
||
676 | } |
||
677 | |||
678 | static void pp_line(TCCState *s1, BufferedFile *f, int level) |
||
679 | { |
||
680 | if (s1->ppfp) { |
||
681 | int d = f->line_num - f->line_ref; |
||
682 | if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_NONE |
||
683 | || (level == 0 && f->line_ref && d < 8)) { |
||
684 | while (d > 0) |
||
685 | fputs("\n", s1->ppfp), --d; |
||
686 | } else if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_STD) { |
||
687 | fprintf(s1->ppfp, "#line %d \"%s\"\n", f->line_num, f->filename); |
||
688 | } else { |
||
689 | fprintf(s1->ppfp, "# %d \"%s\"%s\n", f->line_num, f->filename, |
||
690 | level > 0 ? " 1" : level < 0 ? " 2" : ""); |
||
691 | } |
||
692 | } |
||
693 | f->line_ref = f->line_num; |
||
694 | } |
||
695 | |||
696 | static uint8_t *parse_print_comment(uint8_t *p, int is_line_comment) |
||
697 | { |
||
698 | int c, star_count; |
||
699 | FILE *ppfp = tcc_state->ppfp; |
||
700 | |||
701 | if ((file->line_num - file->line_ref) > 0) { |
||
702 | fputc('\n', ppfp); |
||
703 | file->line_ref++; |
||
704 | pp_line(tcc_state, file, 0); |
||
705 | } |
||
706 | if (is_line_comment) |
||
707 | fputs("//", ppfp); else |
||
708 | fputs("/*", ppfp); |
||
709 | |||
710 | star_count = 0; |
||
711 | for(;;) { |
||
712 | c = *p; |
||
713 | if (c == '\\') { |
||
714 | file->buf_ptr = p; |
||
715 | c = handle_eob(); |
||
716 | p = file->buf_ptr; |
||
717 | } |
||
718 | if (c == CH_EOF) { |
||
719 | if (is_line_comment) break; |
||
720 | tcc_error("unexpected end of file in comment"); |
||
721 | } |
||
722 | if (c == '*') |
||
723 | star_count++; |
||
724 | else { |
||
725 | if ((c == '/') && star_count && !is_line_comment) |
||
726 | break; |
||
727 | star_count = 0; |
||
728 | if (c == '\n') { |
||
729 | if (is_line_comment) break; |
||
730 | file->line_num++; |
||
731 | } |
||
732 | } |
||
733 | fputc(c, ppfp); |
||
734 | p++; |
||
735 | } |
||
736 | if (!is_line_comment) { |
||
737 | fputc('/', ppfp); |
||
738 | p++; |
||
739 | file->line_ref = file->line_num; |
||
740 | } |
||
741 | return p; |
||
742 | } |
||
743 | |||
744 | /* single line C++ comments */ |
||
745 | static uint8_t *parse_line_comment(uint8_t *p, int skip) |
||
746 | { |
||
747 | int c; |
||
748 | |||
749 | p++; |
||
750 | if (tcc_state->option_C && !skip) |
||
751 | return parse_print_comment(p, 1); |
||
752 | for(;;) { |
||
753 | c = *p; |
||
754 | redo: |
||
755 | if (c == '\n' || c == CH_EOF) { |
||
756 | break; |
||
757 | } else if (c == '\\') { |
||
758 | file->buf_ptr = p; |
||
759 | c = handle_eob(); |
||
760 | p = file->buf_ptr; |
||
761 | if (c == '\\') { |
||
762 | PEEKC_EOB(c, p); |
||
763 | if (c == '\n') { |
||
764 | file->line_num++; |
||
765 | PEEKC_EOB(c, p); |
||
766 | } else if (c == '\r') { |
||
767 | PEEKC_EOB(c, p); |
||
768 | if (c == '\n') { |
||
769 | file->line_num++; |
||
770 | PEEKC_EOB(c, p); |
||
771 | } |
||
772 | } |
||
773 | } else { |
||
774 | goto redo; |
||
775 | } |
||
776 | } else { |
||
777 | p++; |
||
778 | } |
||
779 | } |
||
780 | return p; |
||
781 | } |
||
782 | |||
783 | /* C comments */ |
||
784 | ST_FUNC uint8_t *parse_comment(uint8_t *p, int skip) |
||
785 | { |
||
786 | int c; |
||
787 | |||
788 | p++; |
||
789 | if (tcc_state->option_C && !skip) |
||
790 | return parse_print_comment(p, 0); |
||
791 | for(;;) { |
||
792 | /* fast skip loop */ |
||
793 | for(;;) { |
||
794 | c = *p; |
||
795 | if (c == '\n' || c == '*' || c == '\\') |
||
796 | break; |
||
797 | p++; |
||
798 | c = *p; |
||
799 | if (c == '\n' || c == '*' || c == '\\') |
||
800 | break; |
||
801 | p++; |
||
802 | } |
||
803 | /* now we can handle all the cases */ |
||
804 | if (c == '\n') { |
||
805 | file->line_num++; |
||
806 | p++; |
||
807 | } else if (c == '*') { |
||
808 | p++; |
||
809 | for(;;) { |
||
810 | c = *p; |
||
811 | if (c == '*') { |
||
812 | p++; |
||
813 | } else if (c == '/') { |
||
814 | goto end_of_comment; |
||
815 | } else if (c == '\\') { |
||
816 | file->buf_ptr = p; |
||
817 | c = handle_eob(); |
||
818 | p = file->buf_ptr; |
||
819 | if (c == CH_EOF) |
||
820 | tcc_error("unexpected end of file in comment"); |
||
821 | if (c == '\\') { |
||
822 | /* skip '\[\r]\n', otherwise just skip the stray */ |
||
823 | while (c == '\\') { |
||
824 | PEEKC_EOB(c, p); |
||
825 | if (c == '\n') { |
||
826 | file->line_num++; |
||
827 | PEEKC_EOB(c, p); |
||
828 | } else if (c == '\r') { |
||
829 | PEEKC_EOB(c, p); |
||
830 | if (c == '\n') { |
||
831 | file->line_num++; |
||
832 | PEEKC_EOB(c, p); |
||
833 | } |
||
834 | } else { |
||
835 | goto after_star; |
||
836 | } |
||
837 | } |
||
838 | } |
||
839 | } else { |
||
840 | break; |
||
841 | } |
||
842 | } |
||
843 | after_star: ; |
||
844 | } else { |
||
845 | /* stray, eob or eof */ |
||
846 | file->buf_ptr = p; |
||
847 | c = handle_eob(); |
||
848 | p = file->buf_ptr; |
||
849 | if (c == CH_EOF) { |
||
850 | tcc_error("unexpected end of file in comment"); |
||
851 | } else if (c == '\\') { |
||
852 | p++; |
||
853 | } |
||
854 | } |
||
855 | } |
||
856 | end_of_comment: |
||
857 | p++; |
||
858 | return p; |
||
859 | } |
||
860 | |||
861 | #define cinp minp |
||
862 | |||
863 | static inline void skip_spaces(void) |
||
864 | { |
||
865 | while (isidnum_table[ch - CH_EOF] & IS_SPC) |
||
866 | cinp(); |
||
867 | } |
||
868 | |||
869 | static inline int check_space(int t, int *spc) |
||
870 | { |
||
871 | if (t < 256 && (isidnum_table[t - CH_EOF] & IS_SPC)) { |
||
872 | if (*spc) |
||
873 | return 1; |
||
874 | *spc = 1; |
||
875 | } else |
||
876 | *spc = 0; |
||
877 | return 0; |
||
878 | } |
||
879 | |||
880 | /* parse a string without interpreting escapes */ |
||
881 | static uint8_t *parse_pp_string(uint8_t *p, |
||
882 | int sep, CString *str) |
||
883 | { |
||
884 | int c; |
||
885 | p++; |
||
886 | for(;;) { |
||
887 | c = *p; |
||
888 | if (c == sep) { |
||
889 | break; |
||
890 | } else if (c == '\\') { |
||
891 | file->buf_ptr = p; |
||
892 | c = handle_eob(); |
||
893 | p = file->buf_ptr; |
||
894 | if (c == CH_EOF) { |
||
895 | unterminated_string: |
||
896 | /* XXX: indicate line number of start of string */ |
||
897 | tcc_error("missing terminating %c character", sep); |
||
898 | } else if (c == '\\') { |
||
899 | /* escape : just skip \[\r]\n */ |
||
900 | PEEKC_EOB(c, p); |
||
901 | if (c == '\n') { |
||
902 | file->line_num++; |
||
903 | p++; |
||
904 | } else if (c == '\r') { |
||
905 | PEEKC_EOB(c, p); |
||
906 | if (c != '\n') |
||
907 | expect("'\n' after '\r'"); |
||
908 | file->line_num++; |
||
909 | p++; |
||
910 | } else if (c == CH_EOF) { |
||
911 | goto unterminated_string; |
||
912 | } else { |
||
913 | if (str) { |
||
914 | cstr_ccat(str, '\\'); |
||
915 | cstr_ccat(str, c); |
||
916 | } |
||
917 | p++; |
||
918 | } |
||
919 | } |
||
920 | } else if (c == '\n') { |
||
921 | file->line_num++; |
||
922 | goto add_char; |
||
923 | } else if (c == '\r') { |
||
924 | PEEKC_EOB(c, p); |
||
925 | if (c != '\n') { |
||
926 | if (str) |
||
927 | cstr_ccat(str, '\r'); |
||
928 | } else { |
||
929 | file->line_num++; |
||
930 | goto add_char; |
||
931 | } |
||
932 | } else { |
||
933 | add_char: |
||
934 | if (str) |
||
935 | cstr_ccat(str, c); |
||
936 | p++; |
||
937 | } |
||
938 | } |
||
939 | p++; |
||
940 | return p; |
||
941 | } |
||
942 | |||
943 | /* skip block of text until #else, #elif or #endif. skip also pairs of |
||
944 | #if/#endif */ |
||
945 | static void preprocess_skip(void) |
||
946 | { |
||
947 | int a, start_of_line, c, in_warn_or_error; |
||
948 | uint8_t *p; |
||
949 | |||
950 | p = file->buf_ptr; |
||
951 | a = 0; |
||
952 | redo_start: |
||
953 | start_of_line = 1; |
||
954 | in_warn_or_error = 0; |
||
955 | for(;;) { |
||
956 | redo_no_start: |
||
957 | c = *p; |
||
958 | switch(c) { |
||
959 | case ' ': |
||
960 | case '\t': |
||
961 | case '\f': |
||
962 | case '\v': |
||
963 | case '\r': |
||
964 | p++; |
||
965 | goto redo_no_start; |
||
966 | case '\n': |
||
967 | file->line_num++; |
||
968 | p++; |
||
969 | goto redo_start; |
||
970 | case '\\': |
||
971 | file->buf_ptr = p; |
||
972 | c = handle_eob(); |
||
973 | if (c == CH_EOF) { |
||
974 | expect("#endif"); |
||
975 | } else if (c == '\\') { |
||
976 | ch = file->buf_ptr[0]; |
||
977 | handle_stray_noerror(); |
||
978 | } |
||
979 | p = file->buf_ptr; |
||
980 | goto redo_no_start; |
||
981 | /* skip strings */ |
||
982 | case '\"': |
||
983 | case '\'': |
||
984 | if (in_warn_or_error) |
||
985 | goto _default; |
||
986 | p = parse_pp_string(p, c, NULL); |
||
987 | break; |
||
988 | /* skip comments */ |
||
989 | case '/': |
||
990 | if (in_warn_or_error) |
||
991 | goto _default; |
||
992 | file->buf_ptr = p; |
||
993 | ch = *p; |
||
994 | minp(); |
||
995 | p = file->buf_ptr; |
||
996 | if (ch == '*') { |
||
997 | p = parse_comment(p,1); |
||
998 | } else if (ch == '/') { |
||
999 | p = parse_line_comment(p,1); |
||
1000 | } |
||
1001 | break; |
||
1002 | case '#': |
||
1003 | p++; |
||
1004 | if (start_of_line) { |
||
1005 | file->buf_ptr = p; |
||
1006 | next_nomacro(); |
||
1007 | p = file->buf_ptr; |
||
1008 | if (a == 0 && |
||
1009 | (tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF)) |
||
1010 | goto the_end; |
||
1011 | if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF) |
||
1012 | a++; |
||
1013 | else if (tok == TOK_ENDIF) |
||
1014 | a--; |
||
1015 | else if( tok == TOK_ERROR || tok == TOK_WARNING) |
||
1016 | in_warn_or_error = 1; |
||
1017 | else if (tok == TOK_LINEFEED) |
||
1018 | goto redo_start; |
||
1019 | else if (parse_flags & PARSE_FLAG_ASM_FILE) |
||
1020 | p = parse_line_comment(p,0); |
||
1021 | } else if (parse_flags & PARSE_FLAG_ASM_FILE) |
||
1022 | p = parse_line_comment(p,0); |
||
1023 | break; |
||
1024 | _default: |
||
1025 | default: |
||
1026 | p++; |
||
1027 | break; |
||
1028 | } |
||
1029 | start_of_line = 0; |
||
1030 | } |
||
1031 | the_end: ; |
||
1032 | file->buf_ptr = p; |
||
1033 | } |
||
1034 | |||
1035 | /* ParseState handling */ |
||
1036 | |||
1037 | /* XXX: currently, no include file info is stored. Thus, we cannot display |
||
1038 | accurate messages if the function or data definition spans multiple |
||
1039 | files */ |
||
1040 | |||
1041 | /* save current parse state in 's' */ |
||
1042 | ST_FUNC void save_parse_state(ParseState *s) |
||
1043 | { |
||
1044 | s->line_num = file->line_num; |
||
1045 | s->macro_ptr = macro_ptr; |
||
1046 | s->tok = tok; |
||
1047 | s->tokc = tokc; |
||
1048 | } |
||
1049 | |||
1050 | /* restore parse state from 's' */ |
||
1051 | ST_FUNC void restore_parse_state(ParseState *s) |
||
1052 | { |
||
1053 | file->line_num = s->line_num; |
||
1054 | macro_ptr = s->macro_ptr; |
||
1055 | tok = s->tok; |
||
1056 | tokc = s->tokc; |
||
1057 | } |
||
1058 | |||
1059 | /* return the number of additional 'ints' necessary to store the |
||
1060 | token */ |
||
1061 | static inline int tok_size(const int *p) |
||
1062 | { |
||
1063 | switch(*p) { |
||
1064 | /* 4 bytes */ |
||
1065 | case TOK_CINT: |
||
1066 | case TOK_CUINT: |
||
1067 | case TOK_CCHAR: |
||
1068 | case TOK_LCHAR: |
||
1069 | case TOK_CFLOAT: |
||
1070 | case TOK_LINENUM: |
||
1071 | return 1 + 1; |
||
1072 | case TOK_STR: |
||
1073 | case TOK_LSTR: |
||
1074 | case TOK_PPNUM: |
||
1075 | case TOK_PPSTR: |
||
1076 | return 1 + ((sizeof(CString) + ((CString *)(p+1))->size + 3) >> 2); |
||
1077 | case TOK_CDOUBLE: |
||
1078 | case TOK_CLLONG: |
||
1079 | case TOK_CULLONG: |
||
1080 | return 1 + 2; |
||
1081 | case TOK_CLDOUBLE: |
||
1082 | return 1 + LDOUBLE_SIZE / 4; |
||
1083 | default: |
||
1084 | return 1 + 0; |
||
1085 | } |
||
1086 | } |
||
1087 | |||
1088 | /* token string handling */ |
||
1089 | |||
1090 | ST_INLN void tok_str_new(TokenString *s) |
||
1091 | { |
||
1092 | s->str = NULL; |
||
1093 | s->len = 0; |
||
1094 | s->allocated_len = 0; |
||
1095 | s->last_line_num = -1; |
||
1096 | } |
||
1097 | |||
1098 | ST_FUNC int *tok_str_dup(TokenString *s) |
||
1099 | { |
||
1100 | int *str; |
||
1101 | |||
1102 | str = tal_realloc(tokstr_alloc, 0, s->len * sizeof(int)); |
||
1103 | memcpy(str, s->str, s->len * sizeof(int)); |
||
1104 | return str; |
||
1105 | } |
||
1106 | |||
1107 | ST_FUNC void tok_str_free(int *str) |
||
1108 | { |
||
1109 | tal_free(tokstr_alloc, str); |
||
1110 | } |
||
1111 | |||
1112 | ST_FUNC int *tok_str_realloc(TokenString *s, int new_size) |
||
1113 | { |
||
1114 | int *str, size; |
||
1115 | |||
1116 | size = s->allocated_len; |
||
1117 | if (size < 16) |
||
1118 | size = 16; |
||
1119 | while (size < new_size) |
||
1120 | size = size * 2; |
||
1121 | TCC_ASSERT((size & (size -1)) == 0); |
||
1122 | if (size > s->allocated_len) { |
||
1123 | str = tal_realloc(tokstr_alloc, s->str, size * sizeof(int)); |
||
1124 | s->allocated_len = size; |
||
1125 | s->str = str; |
||
1126 | } |
||
1127 | return s->str; |
||
1128 | } |
||
1129 | |||
1130 | ST_FUNC void tok_str_add(TokenString *s, int t) |
||
1131 | { |
||
1132 | int len, *str; |
||
1133 | |||
1134 | len = s->len; |
||
1135 | str = s->str; |
||
1136 | if (len >= s->allocated_len) |
||
1137 | str = tok_str_realloc(s, len + 1); |
||
1138 | str[len++] = t; |
||
1139 | s->len = len; |
||
1140 | } |
||
1141 | |||
1142 | static void tok_str_add2(TokenString *s, int t, CValue *cv) |
||
1143 | { |
||
1144 | int len, *str; |
||
1145 | |||
1146 | len = s->len; |
||
1147 | str = s->str; |
||
1148 | |||
1149 | /* allocate space for worst case */ |
||
1150 | if (len + TOK_MAX_SIZE >= s->allocated_len) |
||
1151 | str = tok_str_realloc(s, len + TOK_MAX_SIZE + 1); |
||
1152 | str[len++] = t; |
||
1153 | switch(t) { |
||
1154 | case TOK_CINT: |
||
1155 | case TOK_CUINT: |
||
1156 | case TOK_CCHAR: |
||
1157 | case TOK_LCHAR: |
||
1158 | case TOK_CFLOAT: |
||
1159 | case TOK_LINENUM: |
||
1160 | str[len++] = cv->tab[0]; |
||
1161 | break; |
||
1162 | case TOK_PPNUM: |
||
1163 | case TOK_PPSTR: |
||
1164 | case TOK_STR: |
||
1165 | case TOK_LSTR: |
||
1166 | { |
||
1167 | /* Insert the string into the int array. */ |
||
1168 | size_t nb_words = |
||
1169 | 1 + (cv->str.size + sizeof(int) - 1) / sizeof(int); |
||
1170 | if (len + nb_words >= s->allocated_len) |
||
1171 | str = tok_str_realloc(s, len + nb_words + 1); |
||
1172 | str[len] = cv->str.size; |
||
1173 | memcpy(&str[len + 1], cv->str.data, cv->str.size); |
||
1174 | len += nb_words; |
||
1175 | } |
||
1176 | break; |
||
1177 | case TOK_CDOUBLE: |
||
1178 | case TOK_CLLONG: |
||
1179 | case TOK_CULLONG: |
||
1180 | #if LDOUBLE_SIZE == 8 |
||
1181 | case TOK_CLDOUBLE: |
||
1182 | #endif |
||
1183 | str[len++] = cv->tab[0]; |
||
1184 | str[len++] = cv->tab[1]; |
||
1185 | break; |
||
1186 | #if LDOUBLE_SIZE == 12 |
||
1187 | case TOK_CLDOUBLE: |
||
1188 | str[len++] = cv->tab[0]; |
||
1189 | str[len++] = cv->tab[1]; |
||
1190 | str[len++] = cv->tab[2]; |
||
1191 | #elif LDOUBLE_SIZE == 16 |
||
1192 | case TOK_CLDOUBLE: |
||
1193 | str[len++] = cv->tab[0]; |
||
1194 | str[len++] = cv->tab[1]; |
||
1195 | str[len++] = cv->tab[2]; |
||
1196 | str[len++] = cv->tab[3]; |
||
1197 | #elif LDOUBLE_SIZE != 8 |
||
1198 | #error add long double size support |
||
1199 | #endif |
||
1200 | break; |
||
1201 | default: |
||
1202 | break; |
||
1203 | } |
||
1204 | s->len = len; |
||
1205 | } |
||
1206 | |||
1207 | /* add the current parse token in token string 's' */ |
||
1208 | ST_FUNC void tok_str_add_tok(TokenString *s) |
||
1209 | { |
||
1210 | CValue cval; |
||
1211 | |||
1212 | /* save line number info */ |
||
1213 | if (file->line_num != s->last_line_num) { |
||
1214 | s->last_line_num = file->line_num; |
||
1215 | cval.i = s->last_line_num; |
||
1216 | tok_str_add2(s, TOK_LINENUM, &cval); |
||
1217 | } |
||
1218 | tok_str_add2(s, tok, &tokc); |
||
1219 | } |
||
1220 | |||
1221 | /* get a token from an integer array and increment pointer |
||
1222 | accordingly. we code it as a macro to avoid pointer aliasing. */ |
||
1223 | static inline void TOK_GET(int *t, const int **pp, CValue *cv) |
||
1224 | { |
||
1225 | const int *p = *pp; |
||
1226 | int n, *tab; |
||
1227 | |||
1228 | tab = cv->tab; |
||
1229 | switch(*t = *p++) { |
||
1230 | case TOK_CINT: |
||
1231 | case TOK_CUINT: |
||
1232 | case TOK_CCHAR: |
||
1233 | case TOK_LCHAR: |
||
1234 | case TOK_CFLOAT: |
||
1235 | case TOK_LINENUM: |
||
1236 | tab[0] = *p++; |
||
1237 | break; |
||
1238 | case TOK_STR: |
||
1239 | case TOK_LSTR: |
||
1240 | case TOK_PPNUM: |
||
1241 | case TOK_PPSTR: |
||
1242 | cv->str.size = *p++; |
||
1243 | cv->str.data = p; |
||
1244 | cv->str.data_allocated = 0; |
||
1245 | p += (cv->str.size + sizeof(int) - 1) / sizeof(int); |
||
1246 | break; |
||
1247 | case TOK_CDOUBLE: |
||
1248 | case TOK_CLLONG: |
||
1249 | case TOK_CULLONG: |
||
1250 | n = 2; |
||
1251 | goto copy; |
||
1252 | case TOK_CLDOUBLE: |
||
1253 | #if LDOUBLE_SIZE == 16 |
||
1254 | n = 4; |
||
1255 | #elif LDOUBLE_SIZE == 12 |
||
1256 | n = 3; |
||
1257 | #elif LDOUBLE_SIZE == 8 |
||
1258 | n = 2; |
||
1259 | #else |
||
1260 | # error add long double size support |
||
1261 | #endif |
||
1262 | copy: |
||
1263 | do |
||
1264 | *tab++ = *p++; |
||
1265 | while (--n); |
||
1266 | break; |
||
1267 | default: |
||
1268 | break; |
||
1269 | } |
||
1270 | *pp = p; |
||
1271 | } |
||
1272 | |||
1273 | /* Calling this function is expensive, but it is not possible |
||
1274 | to read a token string backwards. */ |
||
1275 | static int tok_last(const int *str0, const int *str1) |
||
1276 | { |
||
1277 | const int *str = str0; |
||
1278 | int tok = 0; |
||
1279 | CValue cval; |
||
1280 | |||
1281 | while (str < str1) |
||
1282 | TOK_GET(&tok, &str, &cval); |
||
1283 | return tok; |
||
1284 | } |
||
1285 | |||
1286 | static int macro_is_equal(const int *a, const int *b) |
||
1287 | { |
||
1288 | CValue cv; |
||
1289 | int t; |
||
1290 | while (*a && *b) { |
||
1291 | /* first time preallocate static cstr_buf, next time only reset position to start */ |
||
1292 | cstr_reset(&cstr_buf); |
||
1293 | TOK_GET(&t, &a, &cv); |
||
1294 | cstr_cat(&cstr_buf, get_tok_str(t, &cv), 0); |
||
1295 | TOK_GET(&t, &b, &cv); |
||
1296 | if (strcmp(cstr_buf.data, get_tok_str(t, &cv))) |
||
1297 | return 0; |
||
1298 | } |
||
1299 | return !(*a || *b); |
||
1300 | } |
||
1301 | |||
1302 | static void tok_print(const char *msg, const int *str) |
||
1303 | { |
||
1304 | FILE *pr = tcc_state->dffp; |
||
1305 | int t; |
||
1306 | CValue cval; |
||
1307 | |||
1308 | fprintf(pr, "%s ", msg); |
||
1309 | while (str) { |
||
1310 | TOK_GET(&t, &str, &cval); |
||
1311 | if (!t) |
||
1312 | break; |
||
1313 | fprintf(pr,"%s", get_tok_str(t, &cval)); |
||
1314 | } |
||
1315 | fprintf(pr, "\n"); |
||
1316 | } |
||
1317 | |||
1318 | static int define_print_prepared(Sym *s) |
||
1319 | { |
||
1320 | if (!s || !tcc_state->dffp || tcc_state->dflag == 0) |
||
1321 | return 0; |
||
1322 | |||
1323 | if (s->v < TOK_IDENT || s->v >= tok_ident) |
||
1324 | return 0; |
||
1325 | |||
1326 | if (file && tcc_state->dflag == 'D') { |
||
1327 | file->line_num--; |
||
1328 | pp_line(tcc_state, file, 0); |
||
1329 | file->line_ref = ++file->line_num; |
||
1330 | } |
||
1331 | return 1; |
||
1332 | } |
||
1333 | |||
1334 | static void define_print(int v) |
||
1335 | { |
||
1336 | FILE *pr = tcc_state->dffp; |
||
1337 | Sym *s, *a; |
||
1338 | |||
1339 | s = define_find(v); |
||
1340 | if (define_print_prepared(s) == 0) |
||
1341 | return; |
||
1342 | |||
1343 | fprintf(pr, "// #define %s", get_tok_str(v, NULL)); |
||
1344 | if (s->type.t == MACRO_FUNC) { |
||
1345 | a = s->next; |
||
1346 | fprintf(pr,"("); |
||
1347 | if (a) |
||
1348 | for (;;) { |
||
1349 | fprintf(pr,"%s", get_tok_str(a->v & ~SYM_FIELD, NULL)); |
||
1350 | if (!(a = a->next)) |
||
1351 | break; |
||
1352 | fprintf(pr,","); |
||
1353 | } |
||
1354 | fprintf(pr,")"); |
||
1355 | } |
||
1356 | tok_print("", s->d); |
||
1357 | } |
||
1358 | |||
1359 | static void undef_print(int v) |
||
1360 | { |
||
1361 | FILE *pr = tcc_state->dffp; |
||
1362 | Sym *s; |
||
1363 | |||
1364 | s = define_find(v); |
||
1365 | if (define_print_prepared(s) == 0) |
||
1366 | return; |
||
1367 | |||
1368 | fprintf(pr, "// #undef %s\n", get_tok_str(s->v, NULL)); |
||
1369 | } |
||
1370 | |||
1371 | ST_FUNC void print_defines(void) |
||
1372 | { |
||
1373 | Sym *top = define_stack; |
||
1374 | while (top) { |
||
1375 | define_print(top->v); |
||
1376 | top = top->prev; |
||
1377 | } |
||
1378 | } |
||
1379 | |||
1380 | /* defines handling */ |
||
1381 | ST_INLN void define_push(int v, int macro_type, TokenString *str, Sym *first_arg) |
||
1382 | { |
||
1383 | Sym *s; |
||
1384 | |||
1385 | if (str) { |
||
1386 | s = define_find(v); |
||
1387 | if (s && !macro_is_equal(s->d, str->str)) |
||
1388 | tcc_warning("%s redefined", get_tok_str(v, NULL)); |
||
1389 | } |
||
1390 | |||
1391 | s = sym_push2(&define_stack, v, macro_type, 0); |
||
1392 | s->d = str ? tok_str_dup(str) : NULL; |
||
1393 | s->next = first_arg; |
||
1394 | table_ident[v - TOK_IDENT]->sym_define = s; |
||
1395 | } |
||
1396 | |||
1397 | /* undefined a define symbol. Its name is just set to zero */ |
||
1398 | ST_FUNC void define_undef(Sym *s) |
||
1399 | { |
||
1400 | int v = s->v; |
||
1401 | undef_print(v); |
||
1402 | if (v >= TOK_IDENT && v < tok_ident) |
||
1403 | table_ident[v - TOK_IDENT]->sym_define = NULL; |
||
1404 | } |
||
1405 | |||
1406 | ST_INLN Sym *define_find(int v) |
||
1407 | { |
||
1408 | v -= TOK_IDENT; |
||
1409 | if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) |
||
1410 | return NULL; |
||
1411 | return table_ident[v]->sym_define; |
||
1412 | } |
||
1413 | |||
1414 | /* free define stack until top reaches 'b' */ |
||
1415 | ST_FUNC void free_defines(Sym *b) |
||
1416 | { |
||
1417 | Sym *top, *top1; |
||
1418 | int v; |
||
1419 | |||
1420 | top = define_stack; |
||
1421 | while (top != b) { |
||
1422 | top1 = top->prev; |
||
1423 | /* do not free args or predefined defines */ |
||
1424 | if (top->d) |
||
1425 | tok_str_free(top->d); |
||
1426 | v = top->v; |
||
1427 | if (v >= TOK_IDENT && v < tok_ident) |
||
1428 | table_ident[v - TOK_IDENT]->sym_define = NULL; |
||
1429 | sym_free(top); |
||
1430 | top = top1; |
||
1431 | } |
||
1432 | define_stack = b; |
||
1433 | } |
||
1434 | |||
1435 | /* label lookup */ |
||
1436 | ST_FUNC Sym *label_find(int v) |
||
1437 | { |
||
1438 | v -= TOK_IDENT; |
||
1439 | if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) |
||
1440 | return NULL; |
||
1441 | return table_ident[v]->sym_label; |
||
1442 | } |
||
1443 | |||
1444 | ST_FUNC Sym *label_push(Sym **ptop, int v, int flags) |
||
1445 | { |
||
1446 | Sym *s, **ps; |
||
1447 | s = sym_push2(ptop, v, 0, 0); |
||
1448 | s->r = flags; |
||
1449 | ps = &table_ident[v - TOK_IDENT]->sym_label; |
||
1450 | if (ptop == &global_label_stack) { |
||
1451 | /* modify the top most local identifier, so that |
||
1452 | sym_identifier will point to 's' when popped */ |
||
1453 | while (*ps != NULL) |
||
1454 | ps = &(*ps)->prev_tok; |
||
1455 | } |
||
1456 | s->prev_tok = *ps; |
||
1457 | *ps = s; |
||
1458 | return s; |
||
1459 | } |
||
1460 | |||
1461 | /* pop labels until element last is reached. Look if any labels are |
||
1462 | undefined. Define symbols if '&&label' was used. */ |
||
1463 | ST_FUNC void label_pop(Sym **ptop, Sym *slast) |
||
1464 | { |
||
1465 | Sym *s, *s1; |
||
1466 | for(s = *ptop; s != slast; s = s1) { |
||
1467 | s1 = s->prev; |
||
1468 | if (s->r == LABEL_DECLARED) { |
||
1469 | tcc_warning("label '%s' declared but not used", get_tok_str(s->v, NULL)); |
||
1470 | } else if (s->r == LABEL_FORWARD) { |
||
1471 | tcc_error("label '%s' used but not defined", |
||
1472 | get_tok_str(s->v, NULL)); |
||
1473 | } else { |
||
1474 | if (s->c) { |
||
1475 | /* define corresponding symbol. A size of |
||
1476 | 1 is put. */ |
||
1477 | put_extern_sym(s, cur_text_section, s->jnext, 1); |
||
1478 | } |
||
1479 | } |
||
1480 | /* remove label */ |
||
1481 | table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok; |
||
1482 | sym_free(s); |
||
1483 | } |
||
1484 | *ptop = slast; |
||
1485 | } |
||
1486 | |||
1487 | /* eval an expression for #if/#elif */ |
||
1488 | static int expr_preprocess(void) |
||
1489 | { |
||
1490 | int c, t; |
||
1491 | TokenString str; |
||
1492 | |||
1493 | tok_str_new(&str); |
||
1494 | while (tok != TOK_LINEFEED && tok != TOK_EOF) { |
||
1495 | next(); /* do macro subst */ |
||
1496 | if (tok == TOK_DEFINED) { |
||
1497 | next_nomacro(); |
||
1498 | t = tok; |
||
1499 | if (t == '(') |
||
1500 | next_nomacro(); |
||
1501 | c = define_find(tok) != 0; |
||
1502 | if (t == '(') |
||
1503 | next_nomacro(); |
||
1504 | tok = TOK_CINT; |
||
1505 | tokc.i = c; |
||
1506 | } else if (tok >= TOK_IDENT) { |
||
1507 | /* if undefined macro */ |
||
1508 | tok = TOK_CINT; |
||
1509 | tokc.i = 0; |
||
1510 | } |
||
1511 | tok_str_add_tok(&str); |
||
1512 | } |
||
1513 | tok_str_add(&str, -1); /* simulate end of file */ |
||
1514 | tok_str_add(&str, 0); |
||
1515 | /* now evaluate C constant expression */ |
||
1516 | begin_macro(&str, 0); |
||
1517 | next(); |
||
1518 | c = expr_const(); |
||
1519 | end_macro(); |
||
1520 | return c != 0; |
||
1521 | } |
||
1522 | |||
1523 | |||
1524 | /* parse after #define */ |
||
1525 | ST_FUNC void parse_define(void) |
||
1526 | { |
||
1527 | Sym *s, *first, **ps; |
||
1528 | int v, t, varg, is_vaargs, spc; |
||
1529 | int saved_parse_flags = parse_flags; |
||
1530 | |||
1531 | v = tok; |
||
1532 | if (v < TOK_IDENT) |
||
1533 | tcc_error("invalid macro name '%s'", get_tok_str(tok, &tokc)); |
||
1534 | /* XXX: should check if same macro (ANSI) */ |
||
1535 | first = NULL; |
||
1536 | t = MACRO_OBJ; |
||
1537 | /* '(' must be just after macro definition for MACRO_FUNC */ |
||
1538 | parse_flags |= PARSE_FLAG_SPACES; |
||
1539 | next_nomacro_spc(); |
||
1540 | if (tok == '(') { |
||
1541 | /* must be able to parse TOK_DOTS (in asm mode '.' can be part of identifier) */ |
||
1542 | parse_flags &= ~PARSE_FLAG_ASM_FILE; |
||
1543 | isidnum_table['.' - CH_EOF] = 0; |
||
1544 | next_nomacro(); |
||
1545 | ps = &first; |
||
1546 | if (tok != ')') for (;;) { |
||
1547 | varg = tok; |
||
1548 | next_nomacro(); |
||
1549 | is_vaargs = 0; |
||
1550 | if (varg == TOK_DOTS) { |
||
1551 | varg = TOK___VA_ARGS__; |
||
1552 | is_vaargs = 1; |
||
1553 | } else if (tok == TOK_DOTS && gnu_ext) { |
||
1554 | is_vaargs = 1; |
||
1555 | next_nomacro(); |
||
1556 | } |
||
1557 | if (varg < TOK_IDENT) |
||
1558 | bad_list: |
||
1559 | tcc_error("bad macro parameter list"); |
||
1560 | s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0); |
||
1561 | *ps = s; |
||
1562 | ps = &s->next; |
||
1563 | if (tok == ')') |
||
1564 | break; |
||
1565 | if (tok != ',' || is_vaargs) |
||
1566 | goto bad_list; |
||
1567 | next_nomacro(); |
||
1568 | } |
||
1569 | next_nomacro_spc(); |
||
1570 | t = MACRO_FUNC; |
||
1571 | parse_flags |= (saved_parse_flags & PARSE_FLAG_ASM_FILE); |
||
1572 | isidnum_table['.' - CH_EOF] = |
||
1573 | (parse_flags & PARSE_FLAG_ASM_FILE) ? IS_ID : 0; |
||
1574 | } |
||
1575 | |||
1576 | tokstr_buf.len = 0; |
||
1577 | spc = 2; |
||
1578 | parse_flags |= PARSE_FLAG_ACCEPT_STRAYS | PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED; |
||
1579 | while (tok != TOK_LINEFEED && tok != TOK_EOF) { |
||
1580 | /* remove spaces around ## and after '#' */ |
||
1581 | if (TOK_TWOSHARPS == tok) { |
||
1582 | if (2 == spc) |
||
1583 | goto bad_twosharp; |
||
1584 | if (1 == spc) |
||
1585 | --tokstr_buf.len; |
||
1586 | spc = 3; |
||
1587 | } else if ('#' == tok) { |
||
1588 | spc = 4; |
||
1589 | } else if (check_space(tok, &spc)) { |
||
1590 | goto skip; |
||
1591 | } |
||
1592 | tok_str_add2(&tokstr_buf, tok, &tokc); |
||
1593 | skip: |
||
1594 | next_nomacro_spc(); |
||
1595 | } |
||
1596 | |||
1597 | parse_flags = saved_parse_flags; |
||
1598 | if (spc == 1) |
||
1599 | --tokstr_buf.len; /* remove trailing space */ |
||
1600 | tok_str_add(&tokstr_buf, 0); |
||
1601 | if (3 == spc) |
||
1602 | bad_twosharp: |
||
1603 | tcc_error("'##' cannot appear at either end of macro"); |
||
1604 | define_push(v, t, &tokstr_buf, first); |
||
1605 | define_print(v); |
||
1606 | } |
||
1607 | |||
1608 | static inline int hash_cached_include(const char *filename) |
||
1609 | { |
||
1610 | const unsigned char *s; |
||
1611 | unsigned int h; |
||
1612 | |||
1613 | h = TOK_HASH_INIT; |
||
1614 | s = (unsigned char *) filename; |
||
1615 | while (*s) { |
||
1616 | h = TOK_HASH_FUNC(h, *s); |
||
1617 | s++; |
||
1618 | } |
||
1619 | h &= (CACHED_INCLUDES_HASH_SIZE - 1); |
||
1620 | return h; |
||
1621 | } |
||
1622 | |||
1623 | static CachedInclude *search_cached_include(TCCState *s1, const char *filename) |
||
1624 | { |
||
1625 | CachedInclude *e; |
||
1626 | int i, h; |
||
1627 | h = hash_cached_include(filename); |
||
1628 | i = s1->cached_includes_hash[h]; |
||
1629 | for(;;) { |
||
1630 | if (i == 0) |
||
1631 | break; |
||
1632 | e = s1->cached_includes[i - 1]; |
||
1633 | if (0 == PATHCMP(e->filename, filename)) |
||
1634 | return e; |
||
1635 | i = e->hash_next; |
||
1636 | } |
||
1637 | return NULL; |
||
1638 | } |
||
1639 | |||
1640 | static inline void add_cached_include(TCCState *s1, const char *filename, int ifndef_macro) |
||
1641 | { |
||
1642 | CachedInclude *e; |
||
1643 | int h; |
||
1644 | |||
1645 | if (search_cached_include(s1, filename)) |
||
1646 | return; |
||
1647 | #ifdef INC_DEBUG |
||
1648 | printf("adding cached '%s' %s\n", filename, get_tok_str(ifndef_macro, NULL)); |
||
1649 | #endif |
||
1650 | e = tcc_malloc(sizeof(CachedInclude) + strlen(filename)); |
||
1651 | strcpy(e->filename, filename); |
||
1652 | e->ifndef_macro = ifndef_macro; |
||
1653 | dynarray_add((void ***)&s1->cached_includes, &s1->nb_cached_includes, e); |
||
1654 | /* add in hash table */ |
||
1655 | h = hash_cached_include(filename); |
||
1656 | e->hash_next = s1->cached_includes_hash[h]; |
||
1657 | s1->cached_includes_hash[h] = s1->nb_cached_includes; |
||
1658 | } |
||
1659 | |||
1660 | #define ONCE_PREFIX "#ONCE#" |
||
1661 | |||
1662 | static void pragma_parse(TCCState *s1) |
||
1663 | { |
||
1664 | next_nomacro(); |
||
1665 | if (tok == TOK_push_macro || tok == TOK_pop_macro) { |
||
1666 | int t = tok, v; |
||
1667 | Sym *s; |
||
1668 | |||
1669 | if (next(), tok != '(') |
||
1670 | goto pragma_err; |
||
1671 | if (next(), tok != TOK_STR) |
||
1672 | goto pragma_err; |
||
1673 | v = tok_alloc(tokc.str.data, tokc.str.size - 1)->tok; |
||
1674 | if (next(), tok != ')') |
||
1675 | goto pragma_err; |
||
1676 | if (t == TOK_push_macro) { |
||
1677 | while (NULL == (s = define_find(v))) |
||
1678 | define_push(v, 0, NULL, NULL); |
||
1679 | s->type.ref = s; /* set push boundary */ |
||
1680 | } else { |
||
1681 | for (s = define_stack; s; s = s->prev) |
||
1682 | if (s->v == v && s->type.ref == s) { |
||
1683 | s->type.ref = NULL; |
||
1684 | break; |
||
1685 | } |
||
1686 | } |
||
1687 | if (s) |
||
1688 | table_ident[v - TOK_IDENT]->sym_define = s->d ? s : NULL; |
||
1689 | else |
||
1690 | tcc_warning("unbalanced #pragma pop_macro"); |
||
1691 | |||
1692 | } else if (tok == TOK_once) { |
||
1693 | char buf1[sizeof(file->filename) + sizeof(ONCE_PREFIX)]; |
||
1694 | strcpy(buf1, ONCE_PREFIX); |
||
1695 | strcat(buf1, file->filename); |
||
1696 | #ifdef PATH_NOCASE |
||
1697 | strupr(buf1); |
||
1698 | #endif |
||
1699 | add_cached_include(s1, file->filename, tok_alloc(buf1, strlen(buf1))->tok); |
||
1700 | } else if (s1->ppfp) { |
||
1701 | /* tcc -E: keep pragmas below unchanged */ |
||
1702 | unget_tok(' '); |
||
1703 | unget_tok(TOK_PRAGMA); |
||
1704 | unget_tok('#'); |
||
1705 | unget_tok(TOK_LINEFEED); |
||
1706 | |||
1707 | } else if (tok == TOK_pack) { |
||
1708 | /* This may be: |
||
1709 | #pragma pack(1) // set |
||
1710 | #pragma pack() // reset to default |
||
1711 | #pragma pack(push,1) // push & set |
||
1712 | #pragma pack(pop) // restore previous */ |
||
1713 | next(); |
||
1714 | skip('('); |
||
1715 | if (tok == TOK_ASM_pop) { |
||
1716 | next(); |
||
1717 | if (s1->pack_stack_ptr <= s1->pack_stack) { |
||
1718 | stk_error: |
||
1719 | tcc_error("out of pack stack"); |
||
1720 | } |
||
1721 | s1->pack_stack_ptr--; |
||
1722 | } else { |
||
1723 | int val = 0; |
||
1724 | if (tok != ')') { |
||
1725 | if (tok == TOK_ASM_push) { |
||
1726 | next(); |
||
1727 | if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1) |
||
1728 | goto stk_error; |
||
1729 | s1->pack_stack_ptr++; |
||
1730 | skip(','); |
||
1731 | } |
||
1732 | if (tok != TOK_CINT) |
||
1733 | goto pragma_err; |
||
1734 | val = tokc.i; |
||
1735 | if (val < 1 || val > 16 || (val & (val - 1)) != 0) |
||
1736 | goto pragma_err; |
||
1737 | next(); |
||
1738 | } |
||
1739 | *s1->pack_stack_ptr = val; |
||
1740 | } |
||
1741 | if (tok != ')') |
||
1742 | goto pragma_err; |
||
1743 | |||
1744 | } else if (tok == TOK_comment) { |
||
1745 | char *file; |
||
1746 | next(); |
||
1747 | skip('('); |
||
1748 | if (tok != TOK_lib) |
||
1749 | goto pragma_warn; |
||
1750 | next(); |
||
1751 | skip(','); |
||
1752 | if (tok != TOK_STR) |
||
1753 | goto pragma_err; |
||
1754 | file = tcc_strdup((char *)tokc.str.data); |
||
1755 | dynarray_add((void ***)&s1->pragma_libs, &s1->nb_pragma_libs, file); |
||
1756 | next(); |
||
1757 | if (tok != ')') |
||
1758 | goto pragma_err; |
||
1759 | } else { |
||
1760 | pragma_warn: |
||
1761 | if (s1->warn_unsupported) |
||
1762 | tcc_warning("#pragma %s is ignored", get_tok_str(tok, &tokc)); |
||
1763 | } |
||
1764 | return; |
||
1765 | |||
1766 | pragma_err: |
||
1767 | tcc_error("malformed #pragma directive"); |
||
1768 | return; |
||
1769 | } |
||
1770 | |||
1771 | /* is_bof is true if first non space token at beginning of file */ |
||
1772 | ST_FUNC void preprocess(int is_bof) |
||
1773 | { |
||
1774 | TCCState *s1 = tcc_state; |
||
1775 | int i, c, n, saved_parse_flags; |
||
1776 | char buf[1024], *q; |
||
1777 | Sym *s; |
||
1778 | |||
1779 | saved_parse_flags = parse_flags; |
||
1780 | parse_flags = PARSE_FLAG_PREPROCESS |
||
1781 | | PARSE_FLAG_TOK_NUM |
||
1782 | | PARSE_FLAG_TOK_STR |
||
1783 | | PARSE_FLAG_LINEFEED |
||
1784 | | (parse_flags & PARSE_FLAG_ASM_FILE) |
||
1785 | ; |
||
1786 | |||
1787 | next_nomacro(); |
||
1788 | redo: |
||
1789 | switch(tok) { |
||
1790 | case TOK_DEFINE: |
||
1791 | next_nomacro(); |
||
1792 | parse_define(); |
||
1793 | break; |
||
1794 | case TOK_UNDEF: |
||
1795 | next_nomacro(); |
||
1796 | s = define_find(tok); |
||
1797 | /* undefine symbol by putting an invalid name */ |
||
1798 | if (s) |
||
1799 | define_undef(s); |
||
1800 | break; |
||
1801 | case TOK_INCLUDE: |
||
1802 | case TOK_INCLUDE_NEXT: |
||
1803 | ch = file->buf_ptr[0]; |
||
1804 | /* XXX: incorrect if comments : use next_nomacro with a special mode */ |
||
1805 | skip_spaces(); |
||
1806 | if (ch == '<') { |
||
1807 | c = '>'; |
||
1808 | goto read_name; |
||
1809 | } else if (ch == '\"') { |
||
1810 | c = ch; |
||
1811 | read_name: |
||
1812 | inp(); |
||
1813 | q = buf; |
||
1814 | while (ch != c && ch != '\n' && ch != CH_EOF) { |
||
1815 | if ((q - buf) < sizeof(buf) - 1) |
||
1816 | *q++ = ch; |
||
1817 | if (ch == '\\') { |
||
1818 | if (handle_stray_noerror() == 0) |
||
1819 | --q; |
||
1820 | } else |
||
1821 | inp(); |
||
1822 | } |
||
1823 | *q = '\0'; |
||
1824 | minp(); |
||
1825 | #if 0 |
||
1826 | /* eat all spaces and comments after include */ |
||
1827 | /* XXX: slightly incorrect */ |
||
1828 | while (ch1 != '\n' && ch1 != CH_EOF) |
||
1829 | inp(); |
||
1830 | #endif |
||
1831 | } else { |
||
1832 | /* computed #include : either we have only strings or |
||
1833 | we have anything enclosed in '<>' */ |
||
1834 | next(); |
||
1835 | buf[0] = '\0'; |
||
1836 | if (tok == TOK_STR) { |
||
1837 | while (tok != TOK_LINEFEED) { |
||
1838 | if (tok != TOK_STR) { |
||
1839 | include_syntax: |
||
1840 | tcc_error("'#include' expects \"FILENAME\" or |
||
1841 | } |
||
1842 | pstrcat(buf, sizeof(buf), (char *)tokc.str.data); |
||
1843 | next(); |
||
1844 | } |
||
1845 | c = '\"'; |
||
1846 | } else { |
||
1847 | int len; |
||
1848 | while (tok != TOK_LINEFEED) { |
||
1849 | pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc)); |
||
1850 | next(); |
||
1851 | } |
||
1852 | len = strlen(buf); |
||
1853 | /* check syntax and remove '<>' */ |
||
1854 | if (len < 2 || buf[0] != '<' || buf[len - 1] != '>') |
||
1855 | goto include_syntax; |
||
1856 | memmove(buf, buf + 1, len - 2); |
||
1857 | buf[len - 2] = '\0'; |
||
1858 | c = '>'; |
||
1859 | } |
||
1860 | } |
||
1861 | |||
1862 | if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE) |
||
1863 | tcc_error("#include recursion too deep"); |
||
1864 | /* store current file in stack, but increment stack later below */ |
||
1865 | *s1->include_stack_ptr = file; |
||
1866 | i = tok == TOK_INCLUDE_NEXT ? file->include_next_index : 0; |
||
1867 | n = 2 + s1->nb_include_paths + s1->nb_sysinclude_paths; |
||
1868 | for (; i < n; ++i) { |
||
1869 | char buf1[sizeof file->filename]; |
||
1870 | CachedInclude *e; |
||
1871 | const char *path; |
||
1872 | |||
1873 | if (i == 0) { |
||
1874 | /* check absolute include path */ |
||
1875 | if (!IS_ABSPATH(buf)) |
||
1876 | continue; |
||
1877 | buf1[0] = 0; |
||
1878 | |||
1879 | } else if (i == 1) { |
||
1880 | /* search in current dir if "header.h" */ |
||
1881 | if (c != '\"') |
||
1882 | continue; |
||
1883 | path = file->filename; |
||
1884 | pstrncpy(buf1, path, tcc_basename(path) - path); |
||
1885 | |||
1886 | } else { |
||
1887 | /* search in all the include paths */ |
||
1888 | int j = i - 2, k = j - s1->nb_include_paths; |
||
1889 | path = k < 0 ? s1->include_paths[j] : s1->sysinclude_paths[k]; |
||
1890 | if (path == 0) continue; |
||
1891 | pstrcpy(buf1, sizeof(buf1), path); |
||
1892 | pstrcat(buf1, sizeof(buf1), "/"); |
||
1893 | } |
||
1894 | |||
1895 | pstrcat(buf1, sizeof(buf1), buf); |
||
1896 | e = search_cached_include(s1, buf1); |
||
1897 | if (e && define_find(e->ifndef_macro)) { |
||
1898 | /* no need to parse the include because the 'ifndef macro' |
||
1899 | is defined */ |
||
1900 | #ifdef INC_DEBUG |
||
1901 | printf("%s: skipping cached %s\n", file->filename, buf1); |
||
1902 | #endif |
||
1903 | goto include_done; |
||
1904 | } |
||
1905 | |||
1906 | if (tcc_open(s1, buf1) < 0) |
||
1907 | continue; |
||
1908 | |||
1909 | file->include_next_index = i + 1; |
||
1910 | #ifdef INC_DEBUG |
||
1911 | printf("%s: including %s\n", file->prev->filename, file->filename); |
||
1912 | #endif |
||
1913 | /* update target deps */ |
||
1914 | dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps, |
||
1915 | tcc_strdup(buf1)); |
||
1916 | /* push current file in stack */ |
||
1917 | ++s1->include_stack_ptr; |
||
1918 | /* add include file debug info */ |
||
1919 | if (s1->do_debug) |
||
1920 | put_stabs(file->filename, N_BINCL, 0, 0, 0); |
||
1921 | tok_flags |= TOK_FLAG_BOF | TOK_FLAG_BOL; |
||
1922 | ch = file->buf_ptr[0]; |
||
1923 | goto the_end; |
||
1924 | } |
||
1925 | tcc_error("include file '%s' not found", buf); |
||
1926 | include_done: |
||
1927 | break; |
||
1928 | case TOK_IFNDEF: |
||
1929 | c = 1; |
||
1930 | goto do_ifdef; |
||
1931 | case TOK_IF: |
||
1932 | c = expr_preprocess(); |
||
1933 | goto do_if; |
||
1934 | case TOK_IFDEF: |
||
1935 | c = 0; |
||
1936 | do_ifdef: |
||
1937 | next_nomacro(); |
||
1938 | if (tok < TOK_IDENT) |
||
1939 | tcc_error("invalid argument for '#if%sdef'", c ? "n" : ""); |
||
1940 | if (is_bof) { |
||
1941 | if (c) { |
||
1942 | #ifdef INC_DEBUG |
||
1943 | printf("#ifndef %s\n", get_tok_str(tok, NULL)); |
||
1944 | #endif |
||
1945 | file->ifndef_macro = tok; |
||
1946 | } |
||
1947 | } |
||
1948 | c = (define_find(tok) != 0) ^ c; |
||
1949 | do_if: |
||
1950 | if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE) |
||
1951 | tcc_error("memory full (ifdef)"); |
||
1952 | *s1->ifdef_stack_ptr++ = c; |
||
1953 | goto test_skip; |
||
1954 | case TOK_ELSE: |
||
1955 | if (s1->ifdef_stack_ptr == s1->ifdef_stack) |
||
1956 | tcc_error("#else without matching #if"); |
||
1957 | if (s1->ifdef_stack_ptr[-1] & 2) |
||
1958 | tcc_error("#else after #else"); |
||
1959 | c = (s1->ifdef_stack_ptr[-1] ^= 3); |
||
1960 | goto test_else; |
||
1961 | case TOK_ELIF: |
||
1962 | if (s1->ifdef_stack_ptr == s1->ifdef_stack) |
||
1963 | tcc_error("#elif without matching #if"); |
||
1964 | c = s1->ifdef_stack_ptr[-1]; |
||
1965 | if (c > 1) |
||
1966 | tcc_error("#elif after #else"); |
||
1967 | /* last #if/#elif expression was true: we skip */ |
||
1968 | if (c == 1) |
||
1969 | goto skip; |
||
1970 | c = expr_preprocess(); |
||
1971 | s1->ifdef_stack_ptr[-1] = c; |
||
1972 | test_else: |
||
1973 | if (s1->ifdef_stack_ptr == file->ifdef_stack_ptr + 1) |
||
1974 | file->ifndef_macro = 0; |
||
1975 | test_skip: |
||
1976 | if (!(c & 1)) { |
||
1977 | skip: |
||
1978 | preprocess_skip(); |
||
1979 | is_bof = 0; |
||
1980 | goto redo; |
||
1981 | } |
||
1982 | break; |
||
1983 | case TOK_ENDIF: |
||
1984 | if (s1->ifdef_stack_ptr <= file->ifdef_stack_ptr) |
||
1985 | tcc_error("#endif without matching #if"); |
||
1986 | s1->ifdef_stack_ptr--; |
||
1987 | /* '#ifndef macro' was at the start of file. Now we check if |
||
1988 | an '#endif' is exactly at the end of file */ |
||
1989 | if (file->ifndef_macro && |
||
1990 | s1->ifdef_stack_ptr == file->ifdef_stack_ptr) { |
||
1991 | file->ifndef_macro_saved = file->ifndef_macro; |
||
1992 | /* need to set to zero to avoid false matches if another |
||
1993 | #ifndef at middle of file */ |
||
1994 | file->ifndef_macro = 0; |
||
1995 | while (tok != TOK_LINEFEED) |
||
1996 | next_nomacro(); |
||
1997 | tok_flags |= TOK_FLAG_ENDIF; |
||
1998 | goto the_end; |
||
1999 | } |
||
2000 | break; |
||
2001 | case TOK_PPNUM: |
||
2002 | n = strtoul((char*)tokc.str.data, &q, 10); |
||
2003 | goto _line_num; |
||
2004 | case TOK_LINE: |
||
2005 | next(); |
||
2006 | if (tok != TOK_CINT) |
||
2007 | _line_err: |
||
2008 | tcc_error("wrong #line format"); |
||
2009 | n = tokc.i; |
||
2010 | _line_num: |
||
2011 | next(); |
||
2012 | if (tok != TOK_LINEFEED) { |
||
2013 | if (tok == TOK_STR) |
||
2014 | pstrcpy(file->filename, sizeof(file->filename), (char *)tokc.str.data); |
||
2015 | else if (parse_flags & PARSE_FLAG_ASM_FILE) |
||
2016 | break; |
||
2017 | else |
||
2018 | goto _line_err; |
||
2019 | --n; |
||
2020 | } |
||
2021 | if (file->fd > 0) |
||
2022 | total_lines += file->line_num - n; |
||
2023 | file->line_num = n; |
||
2024 | if (s1->do_debug) |
||
2025 | put_stabs(file->filename, N_BINCL, 0, 0, 0); |
||
2026 | break; |
||
2027 | case TOK_ERROR: |
||
2028 | case TOK_WARNING: |
||
2029 | c = tok; |
||
2030 | ch = file->buf_ptr[0]; |
||
2031 | skip_spaces(); |
||
2032 | q = buf; |
||
2033 | while (ch != '\n' && ch != CH_EOF) { |
||
2034 | if ((q - buf) < sizeof(buf) - 1) |
||
2035 | *q++ = ch; |
||
2036 | if (ch == '\\') { |
||
2037 | if (handle_stray_noerror() == 0) |
||
2038 | --q; |
||
2039 | } else |
||
2040 | inp(); |
||
2041 | } |
||
2042 | *q = '\0'; |
||
2043 | if (c == TOK_ERROR) |
||
2044 | tcc_error("#error %s", buf); |
||
2045 | else |
||
2046 | tcc_warning("#warning %s", buf); |
||
2047 | break; |
||
2048 | case TOK_PRAGMA: |
||
2049 | pragma_parse(s1); |
||
2050 | break; |
||
2051 | case TOK_LINEFEED: |
||
2052 | goto the_end; |
||
2053 | default: |
||
2054 | /* ignore gas line comment in an 'S' file. */ |
||
2055 | if (saved_parse_flags & PARSE_FLAG_ASM_FILE) |
||
2056 | goto ignore; |
||
2057 | if (tok == '!' && is_bof) |
||
2058 | /* '!' is ignored at beginning to allow C scripts. */ |
||
2059 | goto ignore; |
||
2060 | tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc)); |
||
2061 | ignore: |
||
2062 | file->buf_ptr = parse_line_comment(file->buf_ptr,0); |
||
2063 | goto the_end; |
||
2064 | } |
||
2065 | /* ignore other preprocess commands or #! for C scripts */ |
||
2066 | while (tok != TOK_LINEFEED) |
||
2067 | next_nomacro(); |
||
2068 | the_end: |
||
2069 | parse_flags = saved_parse_flags; |
||
2070 | } |
||
2071 | |||
2072 | /* evaluate escape codes in a string. */ |
||
2073 | static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long) |
||
2074 | { |
||
2075 | int c, n; |
||
2076 | const uint8_t *p; |
||
2077 | |||
2078 | p = buf; |
||
2079 | for(;;) { |
||
2080 | c = *p; |
||
2081 | if (c == '\0') |
||
2082 | break; |
||
2083 | if (c == '\\') { |
||
2084 | p++; |
||
2085 | /* escape */ |
||
2086 | c = *p; |
||
2087 | switch(c) { |
||
2088 | case '0': case '1': case '2': case '3': |
||
2089 | case '4': case '5': case '6': case '7': |
||
2090 | /* at most three octal digits */ |
||
2091 | n = c - '0'; |
||
2092 | p++; |
||
2093 | c = *p; |
||
2094 | if (isoct(c)) { |
||
2095 | n = n * 8 + c - '0'; |
||
2096 | p++; |
||
2097 | c = *p; |
||
2098 | if (isoct(c)) { |
||
2099 | n = n * 8 + c - '0'; |
||
2100 | p++; |
||
2101 | } |
||
2102 | } |
||
2103 | c = n; |
||
2104 | goto add_char_nonext; |
||
2105 | case 'x': |
||
2106 | case 'u': |
||
2107 | case 'U': |
||
2108 | p++; |
||
2109 | n = 0; |
||
2110 | for(;;) { |
||
2111 | c = *p; |
||
2112 | if (c >= 'a' && c <= 'f') |
||
2113 | c = c - 'a' + 10; |
||
2114 | else if (c >= 'A' && c <= 'F') |
||
2115 | c = c - 'A' + 10; |
||
2116 | else if (isnum(c)) |
||
2117 | c = c - '0'; |
||
2118 | else |
||
2119 | break; |
||
2120 | n = n * 16 + c; |
||
2121 | p++; |
||
2122 | } |
||
2123 | c = n; |
||
2124 | goto add_char_nonext; |
||
2125 | case 'a': |
||
2126 | c = '\a'; |
||
2127 | break; |
||
2128 | case 'b': |
||
2129 | c = '\b'; |
||
2130 | break; |
||
2131 | case 'f': |
||
2132 | c = '\f'; |
||
2133 | break; |
||
2134 | case 'n': |
||
2135 | c = '\n'; |
||
2136 | break; |
||
2137 | case 'r': |
||
2138 | c = '\r'; |
||
2139 | break; |
||
2140 | case 't': |
||
2141 | c = '\t'; |
||
2142 | break; |
||
2143 | case 'v': |
||
2144 | c = '\v'; |
||
2145 | break; |
||
2146 | case 'e': |
||
2147 | if (!gnu_ext) |
||
2148 | goto invalid_escape; |
||
2149 | c = 27; |
||
2150 | break; |
||
2151 | case '\'': |
||
2152 | case '\"': |
||
2153 | case '\\': |
||
2154 | case '?': |
||
2155 | break; |
||
2156 | default: |
||
2157 | invalid_escape: |
||
2158 | if (c >= '!' && c <= '~') |
||
2159 | tcc_warning("unknown escape sequence: \'\\%c\'", c); |
||
2160 | else |
||
2161 | tcc_warning("unknown escape sequence: \'\\x%x\'", c); |
||
2162 | break; |
||
2163 | } |
||
2164 | } |
||
2165 | p++; |
||
2166 | add_char_nonext: |
||
2167 | if (!is_long) |
||
2168 | cstr_ccat(outstr, c); |
||
2169 | else |
||
2170 | cstr_wccat(outstr, c); |
||
2171 | } |
||
2172 | /* add a trailing '\0' */ |
||
2173 | if (!is_long) |
||
2174 | cstr_ccat(outstr, '\0'); |
||
2175 | else |
||
2176 | cstr_wccat(outstr, '\0'); |
||
2177 | } |
||
2178 | |||
2179 | void parse_string(const char *s, int len) |
||
2180 | { |
||
2181 | uint8_t buf[1000], *p = buf; |
||
2182 | int is_long, sep; |
||
2183 | |||
2184 | if ((is_long = *s == 'L')) |
||
2185 | ++s, --len; |
||
2186 | sep = *s++; |
||
2187 | len -= 2; |
||
2188 | if (len >= sizeof buf) |
||
2189 | p = tcc_malloc(len + 1); |
||
2190 | memcpy(p, s, len); |
||
2191 | p[len] = 0; |
||
2192 | |||
2193 | cstr_reset(&tokcstr); |
||
2194 | parse_escape_string(&tokcstr, p, is_long); |
||
2195 | if (p != buf) |
||
2196 | tcc_free(p); |
||
2197 | |||
2198 | if (sep == '\'') { |
||
2199 | int char_size; |
||
2200 | /* XXX: make it portable */ |
||
2201 | if (!is_long) |
||
2202 | char_size = 1; |
||
2203 | else |
||
2204 | char_size = sizeof(nwchar_t); |
||
2205 | if (tokcstr.size <= char_size) |
||
2206 | tcc_error("empty character constant"); |
||
2207 | if (tokcstr.size > 2 * char_size) |
||
2208 | tcc_warning("multi-character character constant"); |
||
2209 | if (!is_long) { |
||
2210 | tokc.i = *(int8_t *)tokcstr.data; |
||
2211 | tok = TOK_CCHAR; |
||
2212 | } else { |
||
2213 | tokc.i = *(nwchar_t *)tokcstr.data; |
||
2214 | tok = TOK_LCHAR; |
||
2215 | } |
||
2216 | } else { |
||
2217 | tokc.str.size = tokcstr.size; |
||
2218 | tokc.str.data = tokcstr.data; |
||
2219 | tokc.str.data_allocated = tokcstr.data_allocated; |
||
2220 | if (!is_long) |
||
2221 | tok = TOK_STR; |
||
2222 | else |
||
2223 | tok = TOK_LSTR; |
||
2224 | } |
||
2225 | } |
||
2226 | |||
2227 | /* we use 64 bit numbers */ |
||
2228 | #define BN_SIZE 2 |
||
2229 | |||
2230 | /* bn = (bn << shift) | or_val */ |
||
2231 | static void bn_lshift(unsigned int *bn, int shift, int or_val) |
||
2232 | { |
||
2233 | int i; |
||
2234 | unsigned int v; |
||
2235 | for(i=0;i |
||
2236 | v = bn[i]; |
||
2237 | bn[i] = (v << shift) | or_val; |
||
2238 | or_val = v >> (32 - shift); |
||
2239 | } |
||
2240 | } |
||
2241 | |||
2242 | static void bn_zero(unsigned int *bn) |
||
2243 | { |
||
2244 | int i; |
||
2245 | for(i=0;i |
||
2246 | bn[i] = 0; |
||
2247 | } |
||
2248 | } |
||
2249 | |||
2250 | /* parse number in null terminated string 'p' and return it in the |
||
2251 | current token */ |
||
2252 | static void parse_number(const char *p) |
||
2253 | { |
||
2254 | int b, t, shift, frac_bits, s, exp_val, ch; |
||
2255 | char *q; |
||
2256 | unsigned int bn[BN_SIZE]; |
||
2257 | double d; |
||
2258 | |||
2259 | /* number */ |
||
2260 | q = token_buf; |
||
2261 | ch = *p++; |
||
2262 | t = ch; |
||
2263 | ch = *p++; |
||
2264 | *q++ = t; |
||
2265 | b = 10; |
||
2266 | if (t == '.') { |
||
2267 | goto float_frac_parse; |
||
2268 | } else if (t == '0') { |
||
2269 | if (ch == 'x' || ch == 'X') { |
||
2270 | q--; |
||
2271 | ch = *p++; |
||
2272 | b = 16; |
||
2273 | } else if (tcc_ext && (ch == 'b' || ch == 'B')) { |
||
2274 | q--; |
||
2275 | ch = *p++; |
||
2276 | b = 2; |
||
2277 | } |
||
2278 | } |
||
2279 | /* parse all digits. cannot check octal numbers at this stage |
||
2280 | because of floating point constants */ |
||
2281 | while (1) { |
||
2282 | if (ch >= 'a' && ch <= 'f') |
||
2283 | t = ch - 'a' + 10; |
||
2284 | else if (ch >= 'A' && ch <= 'F') |
||
2285 | t = ch - 'A' + 10; |
||
2286 | else if (isnum(ch)) |
||
2287 | t = ch - '0'; |
||
2288 | else |
||
2289 | break; |
||
2290 | if (t >= b) |
||
2291 | break; |
||
2292 | if (q >= token_buf + STRING_MAX_SIZE) { |
||
2293 | num_too_long: |
||
2294 | tcc_error("number too long"); |
||
2295 | } |
||
2296 | *q++ = ch; |
||
2297 | ch = *p++; |
||
2298 | } |
||
2299 | if (ch == '.' || |
||
2300 | ((ch == 'e' || ch == 'E') && b == 10) || |
||
2301 | ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) { |
||
2302 | if (b != 10) { |
||
2303 | /* NOTE: strtox should support that for hexa numbers, but |
||
2304 | non ISOC99 libcs do not support it, so we prefer to do |
||
2305 | it by hand */ |
||
2306 | /* hexadecimal or binary floats */ |
||
2307 | /* XXX: handle overflows */ |
||
2308 | *q = '\0'; |
||
2309 | if (b == 16) |
||
2310 | shift = 4; |
||
2311 | else |
||
2312 | shift = 1; |
||
2313 | bn_zero(bn); |
||
2314 | q = token_buf; |
||
2315 | while (1) { |
||
2316 | t = *q++; |
||
2317 | if (t == '\0') { |
||
2318 | break; |
||
2319 | } else if (t >= 'a') { |
||
2320 | t = t - 'a' + 10; |
||
2321 | } else if (t >= 'A') { |
||
2322 | t = t - 'A' + 10; |
||
2323 | } else { |
||
2324 | t = t - '0'; |
||
2325 | } |
||
2326 | bn_lshift(bn, shift, t); |
||
2327 | } |
||
2328 | frac_bits = 0; |
||
2329 | if (ch == '.') { |
||
2330 | ch = *p++; |
||
2331 | while (1) { |
||
2332 | t = ch; |
||
2333 | if (t >= 'a' && t <= 'f') { |
||
2334 | t = t - 'a' + 10; |
||
2335 | } else if (t >= 'A' && t <= 'F') { |
||
2336 | t = t - 'A' + 10; |
||
2337 | } else if (t >= '0' && t <= '9') { |
||
2338 | t = t - '0'; |
||
2339 | } else { |
||
2340 | break; |
||
2341 | } |
||
2342 | if (t >= b) |
||
2343 | tcc_error("invalid digit"); |
||
2344 | bn_lshift(bn, shift, t); |
||
2345 | frac_bits += shift; |
||
2346 | ch = *p++; |
||
2347 | } |
||
2348 | } |
||
2349 | if (ch != 'p' && ch != 'P') |
||
2350 | expect("exponent"); |
||
2351 | ch = *p++; |
||
2352 | s = 1; |
||
2353 | exp_val = 0; |
||
2354 | if (ch == '+') { |
||
2355 | ch = *p++; |
||
2356 | } else if (ch == '-') { |
||
2357 | s = -1; |
||
2358 | ch = *p++; |
||
2359 | } |
||
2360 | if (ch < '0' || ch > '9') |
||
2361 | expect("exponent digits"); |
||
2362 | while (ch >= '0' && ch <= '9') { |
||
2363 | exp_val = exp_val * 10 + ch - '0'; |
||
2364 | ch = *p++; |
||
2365 | } |
||
2366 | exp_val = exp_val * s; |
||
2367 | |||
2368 | /* now we can generate the number */ |
||
2369 | /* XXX: should patch directly float number */ |
||
2370 | d = (double)bn[1] * 4294967296.0 + (double)bn[0]; |
||
2371 | d = ldexp(d, exp_val - frac_bits); |
||
2372 | t = toup(ch); |
||
2373 | if (t == 'F') { |
||
2374 | ch = *p++; |
||
2375 | tok = TOK_CFLOAT; |
||
2376 | /* float : should handle overflow */ |
||
2377 | tokc.f = (float)d; |
||
2378 | } else if (t == 'L') { |
||
2379 | ch = *p++; |
||
2380 | #ifdef TCC_TARGET_PE |
||
2381 | tok = TOK_CDOUBLE; |
||
2382 | tokc.d = d; |
||
2383 | #else |
||
2384 | tok = TOK_CLDOUBLE; |
||
2385 | /* XXX: not large enough */ |
||
2386 | tokc.ld = (long double)d; |
||
2387 | #endif |
||
2388 | } else { |
||
2389 | tok = TOK_CDOUBLE; |
||
2390 | tokc.d = d; |
||
2391 | } |
||
2392 | } else { |
||
2393 | /* decimal floats */ |
||
2394 | if (ch == '.') { |
||
2395 | if (q >= token_buf + STRING_MAX_SIZE) |
||
2396 | goto num_too_long; |
||
2397 | *q++ = ch; |
||
2398 | ch = *p++; |
||
2399 | float_frac_parse: |
||
2400 | while (ch >= '0' && ch <= '9') { |
||
2401 | if (q >= token_buf + STRING_MAX_SIZE) |
||
2402 | goto num_too_long; |
||
2403 | *q++ = ch; |
||
2404 | ch = *p++; |
||
2405 | } |
||
2406 | } |
||
2407 | if (ch == 'e' || ch == 'E') { |
||
2408 | if (q >= token_buf + STRING_MAX_SIZE) |
||
2409 | goto num_too_long; |
||
2410 | *q++ = ch; |
||
2411 | ch = *p++; |
||
2412 | if (ch == '-' || ch == '+') { |
||
2413 | if (q >= token_buf + STRING_MAX_SIZE) |
||
2414 | goto num_too_long; |
||
2415 | *q++ = ch; |
||
2416 | ch = *p++; |
||
2417 | } |
||
2418 | if (ch < '0' || ch > '9') |
||
2419 | expect("exponent digits"); |
||
2420 | while (ch >= '0' && ch <= '9') { |
||
2421 | if (q >= token_buf + STRING_MAX_SIZE) |
||
2422 | goto num_too_long; |
||
2423 | *q++ = ch; |
||
2424 | ch = *p++; |
||
2425 | } |
||
2426 | } |
||
2427 | *q = '\0'; |
||
2428 | t = toup(ch); |
||
2429 | errno = 0; |
||
2430 | if (t == 'F') { |
||
2431 | ch = *p++; |
||
2432 | tok = TOK_CFLOAT; |
||
2433 | tokc.f = strtof(token_buf, NULL); |
||
2434 | } else if (t == 'L') { |
||
2435 | ch = *p++; |
||
2436 | #ifdef TCC_TARGET_PE |
||
2437 | tok = TOK_CDOUBLE; |
||
2438 | tokc.d = strtod(token_buf, NULL); |
||
2439 | #else |
||
2440 | tok = TOK_CLDOUBLE; |
||
2441 | tokc.ld = strtold(token_buf, NULL); |
||
2442 | #endif |
||
2443 | } else { |
||
2444 | tok = TOK_CDOUBLE; |
||
2445 | tokc.d = strtod(token_buf, NULL); |
||
2446 | } |
||
2447 | } |
||
2448 | } else { |
||
2449 | unsigned long long n, n1; |
||
2450 | int lcount, ucount, must_64bit; |
||
2451 | const char *p1; |
||
2452 | |||
2453 | /* integer number */ |
||
2454 | *q = '\0'; |
||
2455 | q = token_buf; |
||
2456 | if (b == 10 && *q == '0') { |
||
2457 | b = 8; |
||
2458 | q++; |
||
2459 | } |
||
2460 | n = 0; |
||
2461 | while(1) { |
||
2462 | t = *q++; |
||
2463 | /* no need for checks except for base 10 / 8 errors */ |
||
2464 | if (t == '\0') |
||
2465 | break; |
||
2466 | else if (t >= 'a') |
||
2467 | t = t - 'a' + 10; |
||
2468 | else if (t >= 'A') |
||
2469 | t = t - 'A' + 10; |
||
2470 | else |
||
2471 | t = t - '0'; |
||
2472 | if (t >= b) |
||
2473 | tcc_error("invalid digit"); |
||
2474 | n1 = n; |
||
2475 | n = n * b + t; |
||
2476 | /* detect overflow */ |
||
2477 | /* XXX: this test is not reliable */ |
||
2478 | if (n < n1) |
||
2479 | tcc_error("integer constant overflow"); |
||
2480 | } |
||
2481 | |||
2482 | /* Determine the characteristics (unsigned and/or 64bit) the type of |
||
2483 | the constant must have according to the constant suffix(es) */ |
||
2484 | lcount = ucount = must_64bit = 0; |
||
2485 | p1 = p; |
||
2486 | for(;;) { |
||
2487 | t = toup(ch); |
||
2488 | if (t == 'L') { |
||
2489 | if (lcount >= 2) |
||
2490 | tcc_error("three 'l's in integer constant"); |
||
2491 | if (lcount && *(p - 1) != ch) |
||
2492 | tcc_error("incorrect integer suffix: %s", p1); |
||
2493 | lcount++; |
||
2494 | #if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE |
||
2495 | if (lcount == 2) |
||
2496 | #endif |
||
2497 | must_64bit = 1; |
||
2498 | ch = *p++; |
||
2499 | } else if (t == 'U') { |
||
2500 | if (ucount >= 1) |
||
2501 | tcc_error("two 'u's in integer constant"); |
||
2502 | ucount++; |
||
2503 | ch = *p++; |
||
2504 | } else { |
||
2505 | break; |
||
2506 | } |
||
2507 | } |
||
2508 | |||
2509 | /* Whether 64 bits are needed to hold the constant's value */ |
||
2510 | if (n & 0xffffffff00000000LL || must_64bit) { |
||
2511 | tok = TOK_CLLONG; |
||
2512 | n1 = n >> 32; |
||
2513 | } else { |
||
2514 | tok = TOK_CINT; |
||
2515 | n1 = n; |
||
2516 | } |
||
2517 | |||
2518 | /* Whether type must be unsigned to hold the constant's value */ |
||
2519 | if (ucount || ((n1 >> 31) && (b != 10))) { |
||
2520 | if (tok == TOK_CLLONG) |
||
2521 | tok = TOK_CULLONG; |
||
2522 | else |
||
2523 | tok = TOK_CUINT; |
||
2524 | /* If decimal and no unsigned suffix, bump to 64 bits or throw error */ |
||
2525 | } else if (n1 >> 31) { |
||
2526 | if (tok == TOK_CINT) |
||
2527 | tok = TOK_CLLONG; |
||
2528 | else |
||
2529 | tcc_error("integer constant overflow"); |
||
2530 | } |
||
2531 | |||
2532 | if (tok == TOK_CINT || tok == TOK_CUINT) |
||
2533 | tokc.i = n; |
||
2534 | else |
||
2535 | tokc.i = n; |
||
2536 | } |
||
2537 | if (ch) |
||
2538 | tcc_error("invalid number\n"); |
||
2539 | } |
||
2540 | |||
2541 | |||
2542 | #define PARSE2(c1, tok1, c2, tok2) \ |
||
2543 | case c1: \ |
||
2544 | PEEKC(c, p); \ |
||
2545 | if (c == c2) { \ |
||
2546 | p++; \ |
||
2547 | tok = tok2; \ |
||
2548 | } else { \ |
||
2549 | tok = tok1; \ |
||
2550 | } \ |
||
2551 | break; |
||
2552 | |||
2553 | /* return next token without macro substitution */ |
||
2554 | static inline void next_nomacro1(void) |
||
2555 | { |
||
2556 | int t, c, is_long, len; |
||
2557 | TokenSym *ts; |
||
2558 | uint8_t *p, *p1; |
||
2559 | unsigned int h; |
||
2560 | |||
2561 | p = file->buf_ptr; |
||
2562 | redo_no_start: |
||
2563 | c = *p; |
||
2564 | #if (__TINYC__ || __GNUC__) |
||
2565 | #else |
||
2566 | if (c & 0x80) |
||
2567 | goto parse_ident_fast; |
||
2568 | #endif |
||
2569 | switch(c) { |
||
2570 | case ' ': |
||
2571 | case '\t': |
||
2572 | tok = c; |
||
2573 | p++; |
||
2574 | if (parse_flags & PARSE_FLAG_SPACES) |
||
2575 | goto keep_tok_flags; |
||
2576 | while (isidnum_table[*p - CH_EOF] & IS_SPC) |
||
2577 | ++p; |
||
2578 | goto redo_no_start; |
||
2579 | case '\f': |
||
2580 | case '\v': |
||
2581 | case '\r': |
||
2582 | p++; |
||
2583 | goto redo_no_start; |
||
2584 | case '\\': |
||
2585 | /* first look if it is in fact an end of buffer */ |
||
2586 | c = handle_stray1(p); |
||
2587 | p = file->buf_ptr; |
||
2588 | if (c == '\\') |
||
2589 | goto parse_simple; |
||
2590 | if (c != CH_EOF) |
||
2591 | goto redo_no_start; |
||
2592 | { |
||
2593 | TCCState *s1 = tcc_state; |
||
2594 | if ((parse_flags & PARSE_FLAG_LINEFEED) |
||
2595 | && !(tok_flags & TOK_FLAG_EOF)) { |
||
2596 | tok_flags |= TOK_FLAG_EOF; |
||
2597 | tok = TOK_LINEFEED; |
||
2598 | goto keep_tok_flags; |
||
2599 | } else if (!(parse_flags & PARSE_FLAG_PREPROCESS)) { |
||
2600 | tok = TOK_EOF; |
||
2601 | } else if (s1->ifdef_stack_ptr != file->ifdef_stack_ptr) { |
||
2602 | tcc_error("missing #endif"); |
||
2603 | } else if (s1->include_stack_ptr == s1->include_stack) { |
||
2604 | /* no include left : end of file. */ |
||
2605 | tok = TOK_EOF; |
||
2606 | } else { |
||
2607 | tok_flags &= ~TOK_FLAG_EOF; |
||
2608 | /* pop include file */ |
||
2609 | |||
2610 | /* test if previous '#endif' was after a #ifdef at |
||
2611 | start of file */ |
||
2612 | if (tok_flags & TOK_FLAG_ENDIF) { |
||
2613 | #ifdef INC_DEBUG |
||
2614 | printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL)); |
||
2615 | #endif |
||
2616 | add_cached_include(s1, file->filename, file->ifndef_macro_saved); |
||
2617 | tok_flags &= ~TOK_FLAG_ENDIF; |
||
2618 | } |
||
2619 | |||
2620 | /* add end of include file debug info */ |
||
2621 | if (tcc_state->do_debug) { |
||
2622 | put_stabd(N_EINCL, 0, 0); |
||
2623 | } |
||
2624 | /* pop include stack */ |
||
2625 | tcc_close(); |
||
2626 | s1->include_stack_ptr--; |
||
2627 | p = file->buf_ptr; |
||
2628 | goto redo_no_start; |
||
2629 | } |
||
2630 | } |
||
2631 | break; |
||
2632 | |||
2633 | case '\n': |
||
2634 | file->line_num++; |
||
2635 | tok_flags |= TOK_FLAG_BOL; |
||
2636 | p++; |
||
2637 | maybe_newline: |
||
2638 | if (0 == (parse_flags & PARSE_FLAG_LINEFEED)) |
||
2639 | goto redo_no_start; |
||
2640 | tok = TOK_LINEFEED; |
||
2641 | goto keep_tok_flags; |
||
2642 | |||
2643 | case '#': |
||
2644 | /* XXX: simplify */ |
||
2645 | PEEKC(c, p); |
||
2646 | if ((tok_flags & TOK_FLAG_BOL) && |
||
2647 | (parse_flags & PARSE_FLAG_PREPROCESS)) { |
||
2648 | file->buf_ptr = p; |
||
2649 | preprocess(tok_flags & TOK_FLAG_BOF); |
||
2650 | p = file->buf_ptr; |
||
2651 | goto maybe_newline; |
||
2652 | } else { |
||
2653 | if (c == '#') { |
||
2654 | p++; |
||
2655 | tok = TOK_TWOSHARPS; |
||
2656 | } else { |
||
2657 | if (parse_flags & PARSE_FLAG_ASM_FILE) { |
||
2658 | p = parse_line_comment(p - 1,0); |
||
2659 | goto redo_no_start; |
||
2660 | } else { |
||
2661 | tok = '#'; |
||
2662 | } |
||
2663 | } |
||
2664 | } |
||
2665 | break; |
||
2666 | |||
2667 | /* dollar is allowed to start identifiers when not parsing asm */ |
||
2668 | case '$': |
||
2669 | if (!(isidnum_table[c - CH_EOF] & IS_ID) |
||
2670 | || (parse_flags & PARSE_FLAG_ASM_FILE)) |
||
2671 | goto parse_simple; |
||
2672 | |||
2673 | #if (__TINYC__ || __GNUC__) |
||
2674 | case 'a' ... 'z': |
||
2675 | case 'A' ... 'K': |
||
2676 | case 'M' ... 'Z': |
||
2677 | case '_': |
||
2678 | case 0x80 ... 0xFF: |
||
2679 | #else |
||
2680 | case 'a': case 'b': case 'c': case 'd': |
||
2681 | case 'e': case 'f': case 'g': case 'h': |
||
2682 | case 'i': case 'j': case 'k': case 'l': |
||
2683 | case 'm': case 'n': case 'o': case 'p': |
||
2684 | case 'q': case 'r': case 's': case 't': |
||
2685 | case 'u': case 'v': case 'w': case 'x': |
||
2686 | case 'y': case 'z': |
||
2687 | case 'A': case 'B': case 'C': case 'D': |
||
2688 | case 'E': case 'F': case 'G': case 'H': |
||
2689 | case 'I': case 'J': case 'K': |
||
2690 | case 'M': case 'N': case 'O': case 'P': |
||
2691 | case 'Q': case 'R': case 'S': case 'T': |
||
2692 | case 'U': case 'V': case 'W': case 'X': |
||
2693 | case 'Y': case 'Z': |
||
2694 | case '_': |
||
2695 | #endif |
||
2696 | parse_ident_fast: |
||
2697 | p1 = p; |
||
2698 | h = TOK_HASH_INIT; |
||
2699 | h = TOK_HASH_FUNC(h, c); |
||
2700 | while (c = *++p, isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) |
||
2701 | h = TOK_HASH_FUNC(h, c); |
||
2702 | len = p - p1; |
||
2703 | if (c != '\\') { |
||
2704 | TokenSym **pts; |
||
2705 | |||
2706 | /* fast case : no stray found, so we have the full token |
||
2707 | and we have already hashed it */ |
||
2708 | h &= (TOK_HASH_SIZE - 1); |
||
2709 | pts = &hash_ident[h]; |
||
2710 | for(;;) { |
||
2711 | ts = *pts; |
||
2712 | if (!ts) |
||
2713 | break; |
||
2714 | if (ts->len == len && !memcmp(ts->str, p1, len)) |
||
2715 | goto token_found; |
||
2716 | pts = &(ts->hash_next); |
||
2717 | } |
||
2718 | ts = tok_alloc_new(pts, (char *) p1, len); |
||
2719 | token_found: ; |
||
2720 | } else { |
||
2721 | /* slower case */ |
||
2722 | cstr_reset(&tokcstr); |
||
2723 | cstr_cat(&tokcstr, p1, len); |
||
2724 | p--; |
||
2725 | PEEKC(c, p); |
||
2726 | parse_ident_slow: |
||
2727 | while (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) |
||
2728 | { |
||
2729 | cstr_ccat(&tokcstr, c); |
||
2730 | PEEKC(c, p); |
||
2731 | } |
||
2732 | ts = tok_alloc(tokcstr.data, tokcstr.size); |
||
2733 | } |
||
2734 | tok = ts->tok; |
||
2735 | break; |
||
2736 | case 'L': |
||
2737 | t = p[1]; |
||
2738 | if (t != '\\' && t != '\'' && t != '\"') { |
||
2739 | /* fast case */ |
||
2740 | goto parse_ident_fast; |
||
2741 | } else { |
||
2742 | PEEKC(c, p); |
||
2743 | if (c == '\'' || c == '\"') { |
||
2744 | is_long = 1; |
||
2745 | goto str_const; |
||
2746 | } else { |
||
2747 | cstr_reset(&tokcstr); |
||
2748 | cstr_ccat(&tokcstr, 'L'); |
||
2749 | goto parse_ident_slow; |
||
2750 | } |
||
2751 | } |
||
2752 | break; |
||
2753 | |||
2754 | case '0': case '1': case '2': case '3': |
||
2755 | case '4': case '5': case '6': case '7': |
||
2756 | case '8': case '9': |
||
2757 | cstr_reset(&tokcstr); |
||
2758 | /* after the first digit, accept digits, alpha, '.' or sign if |
||
2759 | prefixed by 'eEpP' */ |
||
2760 | parse_num: |
||
2761 | for(;;) { |
||
2762 | t = c; |
||
2763 | cstr_ccat(&tokcstr, c); |
||
2764 | PEEKC(c, p); |
||
2765 | if (!((isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) |
||
2766 | || c == '.' |
||
2767 | || ((c == '+' || c == '-') |
||
2768 | && (t == 'e' || t == 'E' || t == 'p' || t == 'P') |
||
2769 | ))) |
||
2770 | break; |
||
2771 | } |
||
2772 | /* We add a trailing '\0' to ease parsing */ |
||
2773 | cstr_ccat(&tokcstr, '\0'); |
||
2774 | tokc.str.size = tokcstr.size; |
||
2775 | tokc.str.data = tokcstr.data; |
||
2776 | tokc.str.data_allocated = tokcstr.data_allocated; |
||
2777 | tok = TOK_PPNUM; |
||
2778 | break; |
||
2779 | |||
2780 | case '.': |
||
2781 | /* special dot handling because it can also start a number */ |
||
2782 | PEEKC(c, p); |
||
2783 | if (isnum(c)) { |
||
2784 | cstr_reset(&tokcstr); |
||
2785 | cstr_ccat(&tokcstr, '.'); |
||
2786 | goto parse_num; |
||
2787 | } else if ((parse_flags & PARSE_FLAG_ASM_FILE) |
||
2788 | && (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))) { |
||
2789 | *--p = c = '.'; |
||
2790 | goto parse_ident_fast; |
||
2791 | } else if (c == '.') { |
||
2792 | PEEKC(c, p); |
||
2793 | if (c == '.') { |
||
2794 | p++; |
||
2795 | tok = TOK_DOTS; |
||
2796 | } else { |
||
2797 | *--p = '.'; /* may underflow into file->unget[] */ |
||
2798 | tok = '.'; |
||
2799 | } |
||
2800 | } else { |
||
2801 | tok = '.'; |
||
2802 | } |
||
2803 | break; |
||
2804 | case '\'': |
||
2805 | case '\"': |
||
2806 | is_long = 0; |
||
2807 | str_const: |
||
2808 | cstr_reset(&tokcstr); |
||
2809 | if (is_long) |
||
2810 | cstr_ccat(&tokcstr, 'L'); |
||
2811 | cstr_ccat(&tokcstr, c); |
||
2812 | p = parse_pp_string(p, c, &tokcstr); |
||
2813 | cstr_ccat(&tokcstr, c); |
||
2814 | cstr_ccat(&tokcstr, '\0'); |
||
2815 | tokc.str.size = tokcstr.size; |
||
2816 | tokc.str.data = tokcstr.data; |
||
2817 | tokc.str.data_allocated = tokcstr.data_allocated; |
||
2818 | tok = TOK_PPSTR; |
||
2819 | break; |
||
2820 | |||
2821 | case '<': |
||
2822 | PEEKC(c, p); |
||
2823 | if (c == '=') { |
||
2824 | p++; |
||
2825 | tok = TOK_LE; |
||
2826 | } else if (c == '<') { |
||
2827 | PEEKC(c, p); |
||
2828 | if (c == '=') { |
||
2829 | p++; |
||
2830 | tok = TOK_A_SHL; |
||
2831 | } else { |
||
2832 | tok = TOK_SHL; |
||
2833 | } |
||
2834 | } else { |
||
2835 | tok = TOK_LT; |
||
2836 | } |
||
2837 | break; |
||
2838 | case '>': |
||
2839 | PEEKC(c, p); |
||
2840 | if (c == '=') { |
||
2841 | p++; |
||
2842 | tok = TOK_GE; |
||
2843 | } else if (c == '>') { |
||
2844 | PEEKC(c, p); |
||
2845 | if (c == '=') { |
||
2846 | p++; |
||
2847 | tok = TOK_A_SAR; |
||
2848 | } else { |
||
2849 | tok = TOK_SAR; |
||
2850 | } |
||
2851 | } else { |
||
2852 | tok = TOK_GT; |
||
2853 | } |
||
2854 | break; |
||
2855 | |||
2856 | case '&': |
||
2857 | PEEKC(c, p); |
||
2858 | if (c == '&') { |
||
2859 | p++; |
||
2860 | tok = TOK_LAND; |
||
2861 | } else if (c == '=') { |
||
2862 | p++; |
||
2863 | tok = TOK_A_AND; |
||
2864 | } else { |
||
2865 | tok = '&'; |
||
2866 | } |
||
2867 | break; |
||
2868 | |||
2869 | case '|': |
||
2870 | PEEKC(c, p); |
||
2871 | if (c == '|') { |
||
2872 | p++; |
||
2873 | tok = TOK_LOR; |
||
2874 | } else if (c == '=') { |
||
2875 | p++; |
||
2876 | tok = TOK_A_OR; |
||
2877 | } else { |
||
2878 | tok = '|'; |
||
2879 | } |
||
2880 | break; |
||
2881 | |||
2882 | case '+': |
||
2883 | PEEKC(c, p); |
||
2884 | if (c == '+') { |
||
2885 | p++; |
||
2886 | tok = TOK_INC; |
||
2887 | } else if (c == '=') { |
||
2888 | p++; |
||
2889 | tok = TOK_A_ADD; |
||
2890 | } else { |
||
2891 | tok = '+'; |
||
2892 | } |
||
2893 | break; |
||
2894 | |||
2895 | case '-': |
||
2896 | PEEKC(c, p); |
||
2897 | if (c == '-') { |
||
2898 | p++; |
||
2899 | tok = TOK_DEC; |
||
2900 | } else if (c == '=') { |
||
2901 | p++; |
||
2902 | tok = TOK_A_SUB; |
||
2903 | } else if (c == '>') { |
||
2904 | p++; |
||
2905 | tok = TOK_ARROW; |
||
2906 | } else { |
||
2907 | tok = '-'; |
||
2908 | } |
||
2909 | break; |
||
2910 | |||
2911 | PARSE2('!', '!', '=', TOK_NE) |
||
2912 | PARSE2('=', '=', '=', TOK_EQ) |
||
2913 | PARSE2('*', '*', '=', TOK_A_MUL) |
||
2914 | PARSE2('%', '%', '=', TOK_A_MOD) |
||
2915 | PARSE2('^', '^', '=', TOK_A_XOR) |
||
2916 | |||
2917 | /* comments or operator */ |
||
2918 | case '/': |
||
2919 | PEEKC(c, p); |
||
2920 | if (c == '*') { |
||
2921 | p = parse_comment(p,0); |
||
2922 | /* comments replaced by a blank */ |
||
2923 | tok = ' '; |
||
2924 | if (tcc_state->option_C) |
||
2925 | goto redo_no_start; |
||
2926 | goto keep_tok_flags; |
||
2927 | } else if (c == '/') { |
||
2928 | p = parse_line_comment(p,0); |
||
2929 | tok = ' '; |
||
2930 | if (tcc_state->option_C) |
||
2931 | goto redo_no_start; |
||
2932 | goto keep_tok_flags; |
||
2933 | } else if (c == '=') { |
||
2934 | p++; |
||
2935 | tok = TOK_A_DIV; |
||
2936 | } else { |
||
2937 | tok = '/'; |
||
2938 | } |
||
2939 | break; |
||
2940 | |||
2941 | /* simple tokens */ |
||
2942 | case '(': |
||
2943 | case ')': |
||
2944 | case '[': |
||
2945 | case ']': |
||
2946 | case '{': |
||
2947 | case '}': |
||
2948 | case ',': |
||
2949 | case ';': |
||
2950 | case ':': |
||
2951 | case '?': |
||
2952 | case '~': |
||
2953 | case '@': /* only used in assembler */ |
||
2954 | parse_simple: |
||
2955 | tok = c; |
||
2956 | p++; |
||
2957 | break; |
||
2958 | default: |
||
2959 | if (parse_flags & PARSE_FLAG_ASM_FILE) |
||
2960 | goto parse_simple; |
||
2961 | tcc_error("unrecognized character \\x%02x", c); |
||
2962 | break; |
||
2963 | } |
||
2964 | tok_flags = 0; |
||
2965 | keep_tok_flags: |
||
2966 | file->buf_ptr = p; |
||
2967 | #if defined(PARSE_DEBUG) |
||
2968 | printf("token = %s\n", get_tok_str(tok, &tokc)); |
||
2969 | #endif |
||
2970 | } |
||
2971 | |||
2972 | /* return next token without macro substitution. Can read input from |
||
2973 | macro_ptr buffer */ |
||
2974 | static void next_nomacro_spc(void) |
||
2975 | { |
||
2976 | if (macro_ptr) { |
||
2977 | redo: |
||
2978 | tok = *macro_ptr; |
||
2979 | if (tok) { |
||
2980 | TOK_GET(&tok, ¯o_ptr, &tokc); |
||
2981 | if (tok == TOK_LINENUM) { |
||
2982 | file->line_num = tokc.i; |
||
2983 | goto redo; |
||
2984 | } |
||
2985 | } |
||
2986 | } else { |
||
2987 | next_nomacro1(); |
||
2988 | } |
||
2989 | } |
||
2990 | |||
2991 | ST_FUNC void next_nomacro(void) |
||
2992 | { |
||
2993 | do { |
||
2994 | next_nomacro_spc(); |
||
2995 | } while (tok < 256 && (isidnum_table[tok - CH_EOF] & IS_SPC)); |
||
2996 | } |
||
2997 | |||
2998 | |||
2999 | static void macro_subst( |
||
3000 | TokenString *tok_str, |
||
3001 | Sym **nested_list, |
||
3002 | const int *macro_str, |
||
3003 | int can_read_stream |
||
3004 | ); |
||
3005 | |||
3006 | /* substitute arguments in replacement lists in macro_str by the values in |
||
3007 | args (field d) and return allocated string */ |
||
3008 | static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) |
||
3009 | { |
||
3010 | int t, t0, t1, spc; |
||
3011 | const int *st; |
||
3012 | Sym *s; |
||
3013 | CValue cval; |
||
3014 | TokenString str; |
||
3015 | CString cstr; |
||
3016 | |||
3017 | tok_str_new(&str); |
||
3018 | t0 = t1 = 0; |
||
3019 | while(1) { |
||
3020 | TOK_GET(&t, ¯o_str, &cval); |
||
3021 | if (!t) |
||
3022 | break; |
||
3023 | if (t == '#') { |
||
3024 | /* stringize */ |
||
3025 | TOK_GET(&t, ¯o_str, &cval); |
||
3026 | if (!t) |
||
3027 | goto bad_stringy; |
||
3028 | s = sym_find2(args, t); |
||
3029 | if (s) { |
||
3030 | cstr_new(&cstr); |
||
3031 | cstr_ccat(&cstr, '\"'); |
||
3032 | st = s->d; |
||
3033 | spc = 0; |
||
3034 | while (*st) { |
||
3035 | TOK_GET(&t, &st, &cval); |
||
3036 | if (t != TOK_PLCHLDR |
||
3037 | && t != TOK_NOSUBST |
||
3038 | && 0 == check_space(t, &spc)) { |
||
3039 | const char *s = get_tok_str(t, &cval); |
||
3040 | while (*s) { |
||
3041 | if (t == TOK_PPSTR && *s != '\'') |
||
3042 | add_char(&cstr, *s); |
||
3043 | else |
||
3044 | cstr_ccat(&cstr, *s); |
||
3045 | ++s; |
||
3046 | } |
||
3047 | } |
||
3048 | } |
||
3049 | cstr.size -= spc; |
||
3050 | cstr_ccat(&cstr, '\"'); |
||
3051 | cstr_ccat(&cstr, '\0'); |
||
3052 | #ifdef PP_DEBUG |
||
3053 | printf("\nstringize: <%s>\n", (char *)cstr.data); |
||
3054 | #endif |
||
3055 | /* add string */ |
||
3056 | cval.str.size = cstr.size; |
||
3057 | cval.str.data = cstr.data; |
||
3058 | cval.str.data_allocated = cstr.data_allocated; |
||
3059 | tok_str_add2(&str, TOK_PPSTR, &cval); |
||
3060 | cstr_free(&cstr); |
||
3061 | } else { |
||
3062 | bad_stringy: |
||
3063 | expect("macro parameter after '#'"); |
||
3064 | } |
||
3065 | } else if (t >= TOK_IDENT) { |
||
3066 | s = sym_find2(args, t); |
||
3067 | if (s) { |
||
3068 | int l0 = str.len; |
||
3069 | st = s->d; |
||
3070 | /* if '##' is present before or after, no arg substitution */ |
||
3071 | if (*macro_str == TOK_TWOSHARPS || t1 == TOK_TWOSHARPS) { |
||
3072 | /* special case for var arg macros : ## eats the ',' |
||
3073 | if empty VA_ARGS variable. */ |
||
3074 | if (t1 == TOK_TWOSHARPS && t0 == ',' && gnu_ext && s->type.t) { |
||
3075 | if (*st == 0) { |
||
3076 | /* suppress ',' '##' */ |
||
3077 | str.len -= 2; |
||
3078 | } else { |
||
3079 | /* suppress '##' and add variable */ |
||
3080 | str.len--; |
||
3081 | goto add_var; |
||
3082 | } |
||
3083 | } else { |
||
3084 | for(;;) { |
||
3085 | int t1; |
||
3086 | TOK_GET(&t1, &st, &cval); |
||
3087 | if (!t1) |
||
3088 | break; |
||
3089 | tok_str_add2(&str, t1, &cval); |
||
3090 | } |
||
3091 | } |
||
3092 | |||
3093 | } else { |
||
3094 | add_var: |
||
3095 | /* NOTE: the stream cannot be read when macro |
||
3096 | substituing an argument */ |
||
3097 | macro_subst(&str, nested_list, st, 0); |
||
3098 | } |
||
3099 | if (str.len == l0) /* exanded to empty string */ |
||
3100 | tok_str_add(&str, TOK_PLCHLDR); |
||
3101 | } else { |
||
3102 | tok_str_add(&str, t); |
||
3103 | } |
||
3104 | } else { |
||
3105 | tok_str_add2(&str, t, &cval); |
||
3106 | } |
||
3107 | t0 = t1, t1 = t; |
||
3108 | } |
||
3109 | tok_str_add(&str, 0); |
||
3110 | return str.str; |
||
3111 | } |
||
3112 | |||
3113 | static char const ab_month_name[12][4] = |
||
3114 | { |
||
3115 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
||
3116 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
||
3117 | }; |
||
3118 | |||
3119 | /* peek or read [ws_str == NULL] next token from function macro call, |
||
3120 | walking up macro levels up to the file if necessary */ |
||
3121 | static int next_argstream(Sym **nested_list, int can_read_stream, TokenString *ws_str) |
||
3122 | { |
||
3123 | int t; |
||
3124 | const int *p; |
||
3125 | Sym *sa; |
||
3126 | |||
3127 | for (;;) { |
||
3128 | if (macro_ptr) { |
||
3129 | p = macro_ptr, t = *p; |
||
3130 | if (ws_str) { |
||
3131 | while (is_space(t) || TOK_LINEFEED == t) |
||
3132 | tok_str_add(ws_str, t), t = *++p; |
||
3133 | } |
||
3134 | if (t == 0 && can_read_stream) { |
||
3135 | end_macro(); |
||
3136 | /* also, end of scope for nested defined symbol */ |
||
3137 | sa = *nested_list; |
||
3138 | while (sa && sa->v == 0) |
||
3139 | sa = sa->prev; |
||
3140 | if (sa) |
||
3141 | sa->v = 0; |
||
3142 | continue; |
||
3143 | } |
||
3144 | } else { |
||
3145 | ch = handle_eob(); |
||
3146 | if (ws_str) { |
||
3147 | while (is_space(ch) || ch == '\n' || ch == '/') { |
||
3148 | if (ch == '/') { |
||
3149 | int c; |
||
3150 | uint8_t *p = file->buf_ptr; |
||
3151 | PEEKC(c, p); |
||
3152 | if (c == '*') { |
||
3153 | p = parse_comment(p,0); |
||
3154 | file->buf_ptr = p - 1; |
||
3155 | } else if (c == '/') { |
||
3156 | p = parse_line_comment(p,0); |
||
3157 | file->buf_ptr = p - 1; |
||
3158 | } else |
||
3159 | break; |
||
3160 | ch = ' '; |
||
3161 | } |
||
3162 | tok_str_add(ws_str, ch); |
||
3163 | cinp(); |
||
3164 | } |
||
3165 | } |
||
3166 | t = ch; |
||
3167 | } |
||
3168 | |||
3169 | if (ws_str) |
||
3170 | return t; |
||
3171 | next_nomacro_spc(); |
||
3172 | return tok; |
||
3173 | } |
||
3174 | } |
||
3175 | |||
3176 | /* do macro substitution of current token with macro 's' and add |
||
3177 | result to (tok_str,tok_len). 'nested_list' is the list of all |
||
3178 | macros we got inside to avoid recursing. Return non zero if no |
||
3179 | substitution needs to be done */ |
||
3180 | static int macro_subst_tok( |
||
3181 | TokenString *tok_str, |
||
3182 | Sym **nested_list, |
||
3183 | Sym *s, |
||
3184 | int can_read_stream) |
||
3185 | { |
||
3186 | Sym *args, *sa, *sa1; |
||
3187 | int parlevel, *mstr, t, t1, spc; |
||
3188 | TokenString str; |
||
3189 | char *cstrval; |
||
3190 | CValue cval; |
||
3191 | CString cstr; |
||
3192 | char buf[32]; |
||
3193 | |||
3194 | /* if symbol is a macro, prepare substitution */ |
||
3195 | /* special macros */ |
||
3196 | if (tok == TOK___LINE__) { |
||
3197 | snprintf(buf, sizeof(buf), "%d", file->line_num); |
||
3198 | cstrval = buf; |
||
3199 | t1 = TOK_PPNUM; |
||
3200 | goto add_cstr1; |
||
3201 | } else if (tok == TOK___FILE__) { |
||
3202 | cstrval = file->filename; |
||
3203 | goto add_cstr; |
||
3204 | } else if (tok == TOK___DATE__ || tok == TOK___TIME__) { |
||
3205 | time_t ti; |
||
3206 | struct tm *tm; |
||
3207 | |||
3208 | time(&ti); |
||
3209 | tm = localtime(&ti); |
||
3210 | if (tok == TOK___DATE__) { |
||
3211 | snprintf(buf, sizeof(buf), "%s %2d %d", |
||
3212 | ab_month_name[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900); |
||
3213 | } else { |
||
3214 | snprintf(buf, sizeof(buf), "%02d:%02d:%02d", |
||
3215 | tm->tm_hour, tm->tm_min, tm->tm_sec); |
||
3216 | } |
||
3217 | cstrval = buf; |
||
3218 | add_cstr: |
||
3219 | t1 = TOK_STR; |
||
3220 | add_cstr1: |
||
3221 | cstr_new(&cstr); |
||
3222 | cstr_cat(&cstr, cstrval, 0); |
||
3223 | cval.str.size = cstr.size; |
||
3224 | cval.str.data = cstr.data; |
||
3225 | cval.str.data_allocated = cstr.data_allocated; |
||
3226 | tok_str_add2(tok_str, t1, &cval); |
||
3227 | cstr_free(&cstr); |
||
3228 | } else { |
||
3229 | int saved_parse_flags = parse_flags; |
||
3230 | |||
3231 | mstr = s->d; |
||
3232 | if (s->type.t == MACRO_FUNC) { |
||
3233 | /* whitespace between macro name and argument list */ |
||
3234 | TokenString ws_str; |
||
3235 | tok_str_new(&ws_str); |
||
3236 | |||
3237 | spc = 0; |
||
3238 | parse_flags |= PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED |
||
3239 | | PARSE_FLAG_ACCEPT_STRAYS; |
||
3240 | |||
3241 | /* get next token from argument stream */ |
||
3242 | t = next_argstream(nested_list, can_read_stream, &ws_str); |
||
3243 | if (t != '(') { |
||
3244 | /* not a macro substitution after all, restore the |
||
3245 | * macro token plus all whitespace we've read. |
||
3246 | * whitespace is intentionally not merged to preserve |
||
3247 | * newlines. */ |
||
3248 | parse_flags = saved_parse_flags; |
||
3249 | tok_str_add(tok_str, tok); |
||
3250 | if (parse_flags & PARSE_FLAG_SPACES) { |
||
3251 | int i; |
||
3252 | for (i = 0; i < ws_str.len; i++) |
||
3253 | tok_str_add(tok_str, ws_str.str[i]); |
||
3254 | } |
||
3255 | tok_str_free(ws_str.str); |
||
3256 | return 0; |
||
3257 | } else { |
||
3258 | tok_str_free(ws_str.str); |
||
3259 | } |
||
3260 | next_nomacro(); /* eat '(' */ |
||
3261 | |||
3262 | /* argument macro */ |
||
3263 | args = NULL; |
||
3264 | sa = s->next; |
||
3265 | /* NOTE: empty args are allowed, except if no args */ |
||
3266 | for(;;) { |
||
3267 | do { |
||
3268 | next_argstream(nested_list, can_read_stream, NULL); |
||
3269 | } while (is_space(tok) || TOK_LINEFEED == tok); |
||
3270 | empty_arg: |
||
3271 | /* handle '()' case */ |
||
3272 | if (!args && !sa && tok == ')') |
||
3273 | break; |
||
3274 | if (!sa) |
||
3275 | tcc_error("macro '%s' used with too many args", |
||
3276 | get_tok_str(s->v, 0)); |
||
3277 | tok_str_new(&str); |
||
3278 | parlevel = spc = 0; |
||
3279 | /* NOTE: non zero sa->t indicates VA_ARGS */ |
||
3280 | while ((parlevel > 0 || |
||
3281 | (tok != ')' && |
||
3282 | (tok != ',' || sa->type.t)))) { |
||
3283 | if (tok == TOK_EOF || tok == 0) |
||
3284 | break; |
||
3285 | if (tok == '(') |
||
3286 | parlevel++; |
||
3287 | else if (tok == ')') |
||
3288 | parlevel--; |
||
3289 | if (tok == TOK_LINEFEED) |
||
3290 | tok = ' '; |
||
3291 | if (!check_space(tok, &spc)) |
||
3292 | tok_str_add2(&str, tok, &tokc); |
||
3293 | next_argstream(nested_list, can_read_stream, NULL); |
||
3294 | } |
||
3295 | if (parlevel) |
||
3296 | expect(")"); |
||
3297 | str.len -= spc; |
||
3298 | tok_str_add(&str, 0); |
||
3299 | sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0); |
||
3300 | sa1->d = str.str; |
||
3301 | sa = sa->next; |
||
3302 | if (tok == ')') { |
||
3303 | /* special case for gcc var args: add an empty |
||
3304 | var arg argument if it is omitted */ |
||
3305 | if (sa && sa->type.t && gnu_ext) |
||
3306 | goto empty_arg; |
||
3307 | break; |
||
3308 | } |
||
3309 | if (tok != ',') |
||
3310 | expect(","); |
||
3311 | } |
||
3312 | if (sa) { |
||
3313 | tcc_error("macro '%s' used with too few args", |
||
3314 | get_tok_str(s->v, 0)); |
||
3315 | } |
||
3316 | |||
3317 | parse_flags = saved_parse_flags; |
||
3318 | |||
3319 | /* now subst each arg */ |
||
3320 | mstr = macro_arg_subst(nested_list, mstr, args); |
||
3321 | /* free memory */ |
||
3322 | sa = args; |
||
3323 | while (sa) { |
||
3324 | sa1 = sa->prev; |
||
3325 | tok_str_free(sa->d); |
||
3326 | sym_free(sa); |
||
3327 | sa = sa1; |
||
3328 | } |
||
3329 | } |
||
3330 | |||
3331 | sym_push2(nested_list, s->v, 0, 0); |
||
3332 | parse_flags = saved_parse_flags; |
||
3333 | macro_subst(tok_str, nested_list, mstr, can_read_stream | 2); |
||
3334 | |||
3335 | /* pop nested defined symbol */ |
||
3336 | sa1 = *nested_list; |
||
3337 | *nested_list = sa1->prev; |
||
3338 | sym_free(sa1); |
||
3339 | if (mstr != s->d) |
||
3340 | tok_str_free(mstr); |
||
3341 | } |
||
3342 | return 0; |
||
3343 | } |
||
3344 | |||
3345 | int paste_tokens(int t1, CValue *v1, int t2, CValue *v2) |
||
3346 | { |
||
3347 | CString cstr; |
||
3348 | int n; |
||
3349 | |||
3350 | cstr_new(&cstr); |
||
3351 | if (t1 != TOK_PLCHLDR) |
||
3352 | cstr_cat(&cstr, get_tok_str(t1, v1), -1); |
||
3353 | n = cstr.size; |
||
3354 | if (t2 != TOK_PLCHLDR) |
||
3355 | cstr_cat(&cstr, get_tok_str(t2, v2), -1); |
||
3356 | cstr_ccat(&cstr, '\0'); |
||
3357 | |||
3358 | tcc_open_bf(tcc_state, ":paste:", cstr.size); |
||
3359 | memcpy(file->buffer, cstr.data, cstr.size); |
||
3360 | for (;;) { |
||
3361 | next_nomacro1(); |
||
3362 | if (0 == *file->buf_ptr) |
||
3363 | break; |
||
3364 | if (is_space(tok)) |
||
3365 | continue; |
||
3366 | tcc_warning("pasting <%.*s> and <%s> does not give a valid preprocessing token", |
||
3367 | n, cstr.data, (char*)cstr.data + n); |
||
3368 | break; |
||
3369 | } |
||
3370 | tcc_close(); |
||
3371 | |||
3372 | //printf("paste <%s>\n", (char*)cstr.data); |
||
3373 | cstr_free(&cstr); |
||
3374 | return 0; |
||
3375 | } |
||
3376 | |||
3377 | /* handle the '##' operator. Return NULL if no '##' seen. Otherwise |
||
3378 | return the resulting string (which must be freed). */ |
||
3379 | static inline int *macro_twosharps(const int *ptr0) |
||
3380 | { |
||
3381 | int t; |
||
3382 | CValue cval; |
||
3383 | TokenString macro_str1; |
||
3384 | int start_of_nosubsts = -1; |
||
3385 | const int *ptr; |
||
3386 | |||
3387 | /* we search the first '##' */ |
||
3388 | for (ptr = ptr0;;) { |
||
3389 | TOK_GET(&t, &ptr, &cval); |
||
3390 | if (t == TOK_TWOSHARPS) |
||
3391 | break; |
||
3392 | if (t == 0) |
||
3393 | return NULL; |
||
3394 | } |
||
3395 | |||
3396 | tok_str_new(¯o_str1); |
||
3397 | |||
3398 | //tok_print(" $$$", ptr0); |
||
3399 | for (ptr = ptr0;;) { |
||
3400 | TOK_GET(&t, &ptr, &cval); |
||
3401 | if (t == 0) |
||
3402 | break; |
||
3403 | if (t == TOK_TWOSHARPS) |
||
3404 | continue; |
||
3405 | while (*ptr == TOK_TWOSHARPS) { |
||
3406 | int t1; CValue cv1; |
||
3407 | /* given 'a##b', remove nosubsts preceding 'a' */ |
||
3408 | if (start_of_nosubsts >= 0) |
||
3409 | macro_str1.len = start_of_nosubsts; |
||
3410 | /* given 'a##b', remove nosubsts preceding 'b' */ |
||
3411 | while ((t1 = *++ptr) == TOK_NOSUBST) |
||
3412 | ; |
||
3413 | if (t1 && t1 != TOK_TWOSHARPS |
||
3414 | && t1 != ':') /* 'a##:' don't build a new token */ |
||
3415 | { |
||
3416 | TOK_GET(&t1, &ptr, &cv1); |
||
3417 | if (t != TOK_PLCHLDR || t1 != TOK_PLCHLDR) { |
||
3418 | paste_tokens(t, &cval, t1, &cv1); |
||
3419 | t = tok, cval = tokc; |
||
3420 | } |
||
3421 | } |
||
3422 | } |
||
3423 | if (t == TOK_NOSUBST) { |
||
3424 | if (start_of_nosubsts < 0) |
||
3425 | start_of_nosubsts = macro_str1.len; |
||
3426 | } else { |
||
3427 | start_of_nosubsts = -1; |
||
3428 | } |
||
3429 | tok_str_add2(¯o_str1, t, &cval); |
||
3430 | } |
||
3431 | tok_str_add(¯o_str1, 0); |
||
3432 | //tok_print(" ###", macro_str1.str); |
||
3433 | return macro_str1.str; |
||
3434 | } |
||
3435 | |||
3436 | /* do macro substitution of macro_str and add result to |
||
3437 | (tok_str,tok_len). 'nested_list' is the list of all macros we got |
||
3438 | inside to avoid recursing. */ |
||
3439 | static void macro_subst( |
||
3440 | TokenString *tok_str, |
||
3441 | Sym **nested_list, |
||
3442 | const int *macro_str, |
||
3443 | int can_read_stream |
||
3444 | ) |
||
3445 | { |
||
3446 | Sym *s; |
||
3447 | const int *ptr; |
||
3448 | int t, spc, nosubst; |
||
3449 | CValue cval; |
||
3450 | int *macro_str1 = NULL; |
||
3451 | |||
3452 | /* first scan for '##' operator handling */ |
||
3453 | ptr = macro_str; |
||
3454 | spc = nosubst = 0; |
||
3455 | |||
3456 | /* first scan for '##' operator handling */ |
||
3457 | if (can_read_stream & 1) { |
||
3458 | macro_str1 = macro_twosharps(ptr); |
||
3459 | if (macro_str1) |
||
3460 | ptr = macro_str1; |
||
3461 | } |
||
3462 | |||
3463 | while (1) { |
||
3464 | TOK_GET(&t, &ptr, &cval); |
||
3465 | if (t == 0) |
||
3466 | break; |
||
3467 | |||
3468 | if (t >= TOK_IDENT && 0 == nosubst) { |
||
3469 | s = define_find(t); |
||
3470 | if (s == NULL) |
||
3471 | goto no_subst; |
||
3472 | |||
3473 | /* if nested substitution, do nothing */ |
||
3474 | if (sym_find2(*nested_list, t)) { |
||
3475 | /* and mark it as TOK_NOSUBST, so it doesn't get subst'd again */ |
||
3476 | tok_str_add2(tok_str, TOK_NOSUBST, NULL); |
||
3477 | goto no_subst; |
||
3478 | } |
||
3479 | |||
3480 | { |
||
3481 | TokenString str; |
||
3482 | str.str = (int*)ptr; |
||
3483 | begin_macro(&str, 2); |
||
3484 | |||
3485 | tok = t; |
||
3486 | macro_subst_tok(tok_str, nested_list, s, can_read_stream); |
||
3487 | |||
3488 | if (str.alloc == 3) { |
||
3489 | /* already finished by reading function macro arguments */ |
||
3490 | break; |
||
3491 | } |
||
3492 | |||
3493 | ptr = macro_ptr; |
||
3494 | end_macro (); |
||
3495 | } |
||
3496 | |||
3497 | spc = (tok_str->len && |
||
3498 | is_space(tok_last(tok_str->str, |
||
3499 | tok_str->str + tok_str->len))); |
||
3500 | |||
3501 | } else { |
||
3502 | |||
3503 | if (t == '\\' && !(parse_flags & PARSE_FLAG_ACCEPT_STRAYS)) |
||
3504 | tcc_error("stray '\\' in program"); |
||
3505 | |||
3506 | no_subst: |
||
3507 | if (!check_space(t, &spc)) |
||
3508 | tok_str_add2(tok_str, t, &cval); |
||
3509 | nosubst = 0; |
||
3510 | if (t == TOK_NOSUBST) |
||
3511 | nosubst = 1; |
||
3512 | } |
||
3513 | } |
||
3514 | if (macro_str1) |
||
3515 | tok_str_free(macro_str1); |
||
3516 | |||
3517 | } |
||
3518 | |||
3519 | /* return next token with macro substitution */ |
||
3520 | ST_FUNC void next(void) |
||
3521 | { |
||
3522 | redo: |
||
3523 | if (parse_flags & PARSE_FLAG_SPACES) |
||
3524 | next_nomacro_spc(); |
||
3525 | else |
||
3526 | next_nomacro(); |
||
3527 | |||
3528 | if (macro_ptr) { |
||
3529 | if (tok == TOK_NOSUBST || tok == TOK_PLCHLDR) { |
||
3530 | /* discard preprocessor markers */ |
||
3531 | goto redo; |
||
3532 | } else if (tok == 0) { |
||
3533 | /* end of macro or unget token string */ |
||
3534 | end_macro(); |
||
3535 | goto redo; |
||
3536 | } |
||
3537 | } else if (tok >= TOK_IDENT && (parse_flags & PARSE_FLAG_PREPROCESS)) { |
||
3538 | Sym *s; |
||
3539 | /* if reading from file, try to substitute macros */ |
||
3540 | s = define_find(tok); |
||
3541 | if (s) { |
||
3542 | Sym *nested_list = NULL; |
||
3543 | tokstr_buf.len = 0; |
||
3544 | nested_list = NULL; |
||
3545 | macro_subst_tok(&tokstr_buf, &nested_list, s, 1); |
||
3546 | tok_str_add(&tokstr_buf, 0); |
||
3547 | begin_macro(&tokstr_buf, 2); |
||
3548 | goto redo; |
||
3549 | } |
||
3550 | } |
||
3551 | /* convert preprocessor tokens into C tokens */ |
||
3552 | if (tok == TOK_PPNUM) { |
||
3553 | if (parse_flags & PARSE_FLAG_TOK_NUM) |
||
3554 | parse_number((char *)tokc.str.data); |
||
3555 | } else if (tok == TOK_PPSTR) { |
||
3556 | if (parse_flags & PARSE_FLAG_TOK_STR) |
||
3557 | parse_string((char *)tokc.str.data, tokc.str.size - 1); |
||
3558 | } |
||
3559 | } |
||
3560 | |||
3561 | /* push back current token and set current token to 'last_tok'. Only |
||
3562 | identifier case handled for labels. */ |
||
3563 | ST_INLN void unget_tok(int last_tok) |
||
3564 | { |
||
3565 | TokenString *str = tcc_malloc(sizeof *str); |
||
3566 | tok_str_new(str); |
||
3567 | tok_str_add2(str, tok, &tokc); |
||
3568 | tok_str_add(str, 0); |
||
3569 | begin_macro(str, 1); |
||
3570 | tok = last_tok; |
||
3571 | } |
||
3572 | |||
3573 | /* better than nothing, but needs extension to handle '-E' option |
||
3574 | correctly too */ |
||
3575 | ST_FUNC void preprocess_init(TCCState *s1) |
||
3576 | { |
||
3577 | s1->include_stack_ptr = s1->include_stack; |
||
3578 | /* XXX: move that before to avoid having to initialize |
||
3579 | file->ifdef_stack_ptr ? */ |
||
3580 | s1->ifdef_stack_ptr = s1->ifdef_stack; |
||
3581 | file->ifdef_stack_ptr = s1->ifdef_stack_ptr; |
||
3582 | |||
3583 | pvtop = vtop = vstack - 1; |
||
3584 | s1->pack_stack[0] = 0; |
||
3585 | s1->pack_stack_ptr = s1->pack_stack; |
||
3586 | |||
3587 | isidnum_table['$' - CH_EOF] = |
||
3588 | tcc_state->dollars_in_identifiers ? IS_ID : 0; |
||
3589 | |||
3590 | isidnum_table['.' - CH_EOF] = |
||
3591 | (parse_flags & PARSE_FLAG_ASM_FILE) ? IS_ID : 0; |
||
3592 | } |
||
3593 | |||
3594 | ST_FUNC void preprocess_new(void) |
||
3595 | { |
||
3596 | int i, c; |
||
3597 | const char *p, *r; |
||
3598 | |||
3599 | /* init isid table */ |
||
3600 | for(i = CH_EOF; i<128; i++) |
||
3601 | isidnum_table[i - CH_EOF] |
||
3602 | = is_space(i) ? IS_SPC |
||
3603 | : isid(i) ? IS_ID |
||
3604 | : isnum(i) ? IS_NUM |
||
3605 | : 0; |
||
3606 | |||
3607 | for(i = 128; i<256; i++) |
||
3608 | isidnum_table[i - CH_EOF] = IS_ID; |
||
3609 | |||
3610 | /* init allocators */ |
||
3611 | tal_new(&toksym_alloc, TOKSYM_TAL_LIMIT, TOKSYM_TAL_SIZE); |
||
3612 | tal_new(&tokstr_alloc, TOKSTR_TAL_LIMIT, TOKSTR_TAL_SIZE); |
||
3613 | tal_new(&cstr_alloc, CSTR_TAL_LIMIT, CSTR_TAL_SIZE); |
||
3614 | |||
3615 | memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *)); |
||
3616 | cstr_new(&cstr_buf); |
||
3617 | cstr_realloc(&cstr_buf, STRING_MAX_SIZE); |
||
3618 | tok_str_new(&tokstr_buf); |
||
3619 | tok_str_realloc(&tokstr_buf, TOKSTR_MAX_SIZE); |
||
3620 | |||
3621 | tok_ident = TOK_IDENT; |
||
3622 | p = tcc_keywords; |
||
3623 | while (*p) { |
||
3624 | r = p; |
||
3625 | for(;;) { |
||
3626 | c = *r++; |
||
3627 | if (c == '\0') |
||
3628 | break; |
||
3629 | } |
||
3630 | tok_alloc(p, r - p - 1); |
||
3631 | p = r; |
||
3632 | } |
||
3633 | } |
||
3634 | |||
3635 | ST_FUNC void preprocess_delete(void) |
||
3636 | { |
||
3637 | int i, n; |
||
3638 | |||
3639 | /* free -D and compiler defines */ |
||
3640 | free_defines(NULL); |
||
3641 | |||
3642 | /* cleanup from error/setjmp */ |
||
3643 | while (macro_stack) |
||
3644 | end_macro(); |
||
3645 | macro_ptr = NULL; |
||
3646 | |||
3647 | /* free tokens */ |
||
3648 | n = tok_ident - TOK_IDENT; |
||
3649 | for(i = 0; i < n; i++) |
||
3650 | tal_free(toksym_alloc, table_ident[i]); |
||
3651 | tcc_free(table_ident); |
||
3652 | table_ident = NULL; |
||
3653 | |||
3654 | /* free static buffers */ |
||
3655 | cstr_free(&tokcstr); |
||
3656 | cstr_free(&cstr_buf); |
||
3657 | tok_str_free(tokstr_buf.str); |
||
3658 | |||
3659 | /* free allocators */ |
||
3660 | tal_delete(toksym_alloc); |
||
3661 | toksym_alloc = NULL; |
||
3662 | tal_delete(tokstr_alloc); |
||
3663 | tokstr_alloc = NULL; |
||
3664 | tal_delete(cstr_alloc); |
||
3665 | cstr_alloc = NULL; |
||
3666 | } |
||
3667 | |||
3668 | /* Preprocess the current file */ |
||
3669 | ST_FUNC int tcc_preprocess(TCCState *s1) |
||
3670 | { |
||
3671 | BufferedFile **iptr; |
||
3672 | int token_seen, spcs, level; |
||
3673 | |||
3674 | preprocess_init(s1); |
||
3675 | ch = file->buf_ptr[0]; |
||
3676 | tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; |
||
3677 | parse_flags = PARSE_FLAG_PREPROCESS |
||
3678 | | (parse_flags & PARSE_FLAG_ASM_FILE) |
||
3679 | | PARSE_FLAG_LINEFEED |
||
3680 | | PARSE_FLAG_SPACES |
||
3681 | | PARSE_FLAG_ACCEPT_STRAYS |
||
3682 | ; |
||
3683 | |||
3684 | #ifdef PP_BENCH |
||
3685 | do next(); while (tok != TOK_EOF); return 0; |
||
3686 | #endif |
||
3687 | |||
3688 | token_seen = spcs = 0; |
||
3689 | pp_line(s1, file, 0); |
||
3690 | |||
3691 | for (;;) { |
||
3692 | iptr = s1->include_stack_ptr; |
||
3693 | next(); |
||
3694 | if (tok == TOK_EOF) |
||
3695 | break; |
||
3696 | level = s1->include_stack_ptr - iptr; |
||
3697 | if (level) { |
||
3698 | if (level > 0) |
||
3699 | pp_line(s1, *iptr, 0); |
||
3700 | pp_line(s1, file, level); |
||
3701 | } |
||
3702 | |||
3703 | if (0 == token_seen) { |
||
3704 | if (tok == ' ') { |
||
3705 | ++spcs; |
||
3706 | continue; |
||
3707 | } |
||
3708 | if (tok == TOK_LINEFEED) { |
||
3709 | spcs = 0; |
||
3710 | continue; |
||
3711 | } |
||
3712 | pp_line(s1, file, 0); |
||
3713 | while (s1->ppfp && spcs > 0) |
||
3714 | fputs(" ", s1->ppfp), --spcs; |
||
3715 | spcs = 0; |
||
3716 | token_seen = 1; |
||
3717 | |||
3718 | } else if (tok == TOK_LINEFEED) { |
||
3719 | ++file->line_ref; |
||
3720 | token_seen = 0; |
||
3721 | } |
||
3722 | if (s1->ppfp) |
||
3723 | fputs(get_tok_str(tok, &tokc), s1->ppfp); |
||
3724 | } |
||
3725 | |||
3726 | return 0; |
||
3727 | } |
||
3728 | |||
3729 | ST_FUNC char *trimfront(char *p) |
||
3730 | { |
||
3731 | while (*p && (unsigned char)*p <= ' ') |
||
3732 | ++p; |
||
3733 | return p; |
||
3734 | } |
||
3735 | |||
3736 | ST_FUNC char *trimback(char *a, char *e) |
||
3737 | { |
||
3738 | while (e > a && (unsigned char)e[-1] <= ' ') |
||
3739 | --e; |
||
3740 | *e = 0;; |
||
3741 | return a; |
||
3742 | }=>=>>256;>128;>>%s>%s>%.*s>>%s>>')>': |