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