fix parsing of define argument lists, fix subtle problems with first token of include...
[cparser] / preprocessor.c
1 #include <config.h>
2
3 #include "token_t.h"
4 #include "symbol_t.h"
5 #include "adt/util.h"
6 #include "adt/error.h"
7 #include "lang_features.h"
8 #include "diagnostic.h"
9 #include "string_rep.h"
10
11 #include <assert.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <stdbool.h>
15 #include <ctype.h>
16
17 //#define DEBUG_CHARS
18 #define MAX_PUTBACK 3
19 #define INCLUDE_LIMIT 199  /* 199 is for gcc "compatibility" */
20
21 struct pp_definition_t {
22         symbol_t          *symbol;
23         source_position_t  source_position;
24         pp_definition_t   *parent_expansion;
25         size_t             expand_pos;
26         bool               is_variadic   : 1;
27         bool               is_expanding  : 1;
28         bool               has_arguments : 1;
29         size_t             n_arguments;
30         symbol_t          *arguments;
31         size_t             list_len;
32         token_t           *replacement_list;
33 };
34
35 typedef struct pp_conditional_t pp_conditional_t;
36 struct pp_conditional_t {
37         source_position_t  source_position;
38         bool               condition;
39         bool               in_else;
40         bool               skip; /**< conditional in skip mode (then+else gets skipped) */
41         pp_conditional_t  *parent;
42 };
43
44 typedef struct pp_input_t pp_input_t;
45 struct pp_input_t {
46         FILE              *file;
47         int                c;
48         char               buf[1024+MAX_PUTBACK];
49         const char        *bufend;
50         const char        *bufpos;
51         source_position_t  position;
52         bool               had_non_space;
53         pp_input_t        *parent;
54 };
55
56 pp_input_t input;
57 #define CC input.c
58
59 static pp_input_t     *input_stack;
60 static unsigned        n_inputs;
61 static struct obstack  input_obstack;
62
63 static pp_conditional_t *conditional_stack;
64
65 token_t                   pp_token;
66 static bool               resolve_escape_sequences = false;
67 static bool               do_print_spaces          = true;
68 static bool               do_expansions;
69 static bool               skip_mode;
70 static FILE              *out;
71 static struct obstack     pp_obstack;
72 static unsigned           counted_newlines;
73 static unsigned           counted_spaces;
74 static const char        *printed_input_name = NULL;
75 static pp_definition_t   *current_expansion  = NULL;
76
77 static inline void next_char(void);
78 static void next_preprocessing_token(void);
79 static void print_line_directive(const source_position_t *pos, const char *add);
80 static void print_spaces(void);
81
82 static bool open_input(const char *filename)
83 {
84         FILE *file = fopen(filename, "r");
85         if (file == NULL)
86                 return false;
87
88         input.file                = file;
89         input.bufend              = NULL;
90         input.bufpos              = NULL;
91         input.had_non_space       = false;
92         input.position.input_name = filename;
93         input.position.linenr     = 1;
94
95         /* indicate that we're at a new input */
96         print_line_directive(&input.position, input_stack != NULL ? "1" : NULL);
97
98         counted_newlines = 0;
99         counted_spaces   = 0;
100
101         /* read first char and first token */
102         next_char();
103         next_preprocessing_token();
104
105         return true;
106 }
107
108 static void close_input(void)
109 {
110         /* ensure we have a newline at EOF */
111         if (input.had_non_space) {
112                 fputc('\n', out);
113         }
114
115         assert(input.file != NULL);
116
117         fclose(input.file);
118         input.file   = NULL;
119         input.bufend = NULL;
120         input.bufpos = NULL;
121         input.c      = EOF;
122 }
123
124 static void push_input(void)
125 {
126         pp_input_t *saved_input
127                 = obstack_alloc(&input_obstack, sizeof(*saved_input));
128
129         memcpy(saved_input, &input, sizeof(*saved_input));
130
131         /* adjust buffer positions */
132         if (input.bufpos != NULL)
133                 saved_input->bufpos = saved_input->buf + (input.bufpos - input.buf);
134         if (input.bufend != NULL)
135                 saved_input->bufend = saved_input->buf + (input.bufend - input.buf);
136
137         saved_input->parent = input_stack;
138         input_stack         = saved_input;
139         ++n_inputs;
140 }
141
142 static void pop_restore_input(void)
143 {
144         assert(n_inputs > 0);
145         assert(input_stack != NULL);
146
147         pp_input_t *saved_input = input_stack;
148
149         memcpy(&input, saved_input, sizeof(input));
150         input.parent = NULL;
151
152         /* adjust buffer positions */
153         if (saved_input->bufpos != NULL)
154                 input.bufpos = input.buf + (saved_input->bufpos - saved_input->buf);
155         if (saved_input->bufend != NULL)
156                 input.bufend = input.buf + (saved_input->bufend - saved_input->buf);
157
158         input_stack = saved_input->parent;
159         obstack_free(&input_obstack, saved_input);
160         --n_inputs;
161 }
162
163 /**
164  * Prints a parse error message at the current token.
165  *
166  * @param msg   the error message
167  */
168 static void parse_error(const char *msg)
169 {
170         errorf(&pp_token.source_position,  "%s", msg);
171 }
172
173 static inline void next_real_char(void)
174 {
175         assert(input.bufpos <= input.bufend);
176         if (input.bufpos >= input.bufend) {
177                 size_t s = fread(input.buf + MAX_PUTBACK, 1,
178                                  sizeof(input.buf) - MAX_PUTBACK, input.file);
179                 if(s == 0) {
180                         CC = EOF;
181                         return;
182                 }
183                 input.bufpos = input.buf + MAX_PUTBACK;
184                 input.bufend = input.buf + MAX_PUTBACK + s;
185         }
186         CC = *input.bufpos++;
187 }
188
189 /**
190  * Put a character back into the buffer.
191  *
192  * @param pc  the character to put back
193  */
194 static inline void put_back(int pc)
195 {
196         assert(input.bufpos > input.buf);
197         *(--input.bufpos - input.buf + input.buf) = (char) pc;
198
199 #ifdef DEBUG_CHARS
200         printf("putback '%c'\n", pc);
201 #endif
202 }
203
204 #define MATCH_NEWLINE(code)                   \
205         case '\r':                                \
206                 next_char();                          \
207                 if(CC == '\n') {                      \
208                         next_char();                      \
209                 }                                     \
210                 ++input.position.linenr;              \
211                 code                                  \
212         case '\n':                                \
213                 next_char();                          \
214                 ++input.position.linenr;              \
215                 code
216
217 #define eat(c_type)  do { assert(CC == c_type); next_char(); } while(0)
218
219 static void maybe_concat_lines(void)
220 {
221         eat('\\');
222
223         switch(CC) {
224         MATCH_NEWLINE(return;)
225
226         default:
227                 break;
228         }
229
230         put_back(CC);
231         CC = '\\';
232 }
233
234 /**
235  * Set c to the next input character, ie.
236  * after expanding trigraphs.
237  */
238 static inline void next_char(void)
239 {
240         next_real_char();
241
242         /* filter trigraphs and concatenated lines */
243         if(UNLIKELY(CC == '\\')) {
244                 maybe_concat_lines();
245                 goto end_of_next_char;
246         }
247
248         if(LIKELY(CC != '?'))
249                 goto end_of_next_char;
250
251         next_real_char();
252         if(LIKELY(CC != '?')) {
253                 put_back(CC);
254                 CC = '?';
255                 goto end_of_next_char;
256         }
257
258         next_real_char();
259         switch(CC) {
260         case '=': CC = '#'; break;
261         case '(': CC = '['; break;
262         case '/': CC = '\\'; maybe_concat_lines(); break;
263         case ')': CC = ']'; break;
264         case '\'': CC = '^'; break;
265         case '<': CC = '{'; break;
266         case '!': CC = '|'; break;
267         case '>': CC = '}'; break;
268         case '-': CC = '~'; break;
269         default:
270                 put_back(CC);
271                 put_back('?');
272                 CC = '?';
273                 break;
274         }
275
276 end_of_next_char:;
277 #ifdef DEBUG_CHARS
278         printf("nchar '%c'\n", CC);
279 #endif
280 }
281
282
283
284 /**
285  * Returns true if the given char is a octal digit.
286  *
287  * @param char  the character to check
288  */
289 static inline bool is_octal_digit(int chr)
290 {
291         switch(chr) {
292         case '0':
293         case '1':
294         case '2':
295         case '3':
296         case '4':
297         case '5':
298         case '6':
299         case '7':
300                 return true;
301         default:
302                 return false;
303         }
304 }
305
306 /**
307  * Returns the value of a digit.
308  * The only portable way to do it ...
309  */
310 static int digit_value(int digit) {
311         switch (digit) {
312         case '0': return 0;
313         case '1': return 1;
314         case '2': return 2;
315         case '3': return 3;
316         case '4': return 4;
317         case '5': return 5;
318         case '6': return 6;
319         case '7': return 7;
320         case '8': return 8;
321         case '9': return 9;
322         case 'a':
323         case 'A': return 10;
324         case 'b':
325         case 'B': return 11;
326         case 'c':
327         case 'C': return 12;
328         case 'd':
329         case 'D': return 13;
330         case 'e':
331         case 'E': return 14;
332         case 'f':
333         case 'F': return 15;
334         default:
335                 panic("wrong character given");
336         }
337 }
338
339 /**
340  * Parses an octal character sequence.
341  *
342  * @param first_digit  the already read first digit
343  */
344 static int parse_octal_sequence(const int first_digit)
345 {
346         assert(is_octal_digit(first_digit));
347         int value = digit_value(first_digit);
348         if (!is_octal_digit(CC)) return value;
349         value = 8 * value + digit_value(CC);
350         next_char();
351         if (!is_octal_digit(CC)) return value;
352         value = 8 * value + digit_value(CC);
353         next_char();
354
355         if(char_is_signed) {
356                 return (signed char) value;
357         } else {
358                 return (unsigned char) value;
359         }
360 }
361
362 /**
363  * Parses a hex character sequence.
364  */
365 static int parse_hex_sequence(void)
366 {
367         int value = 0;
368         while(isxdigit(CC)) {
369                 value = 16 * value + digit_value(CC);
370                 next_char();
371         }
372
373         if(char_is_signed) {
374                 return (signed char) value;
375         } else {
376                 return (unsigned char) value;
377         }
378 }
379
380 /**
381  * Parse an escape sequence.
382  */
383 static int parse_escape_sequence(void)
384 {
385         eat('\\');
386
387         int ec = CC;
388         next_char();
389
390         switch(ec) {
391         case '"':  return '"';
392         case '\'': return '\'';
393         case '\\': return '\\';
394         case '?': return '\?';
395         case 'a': return '\a';
396         case 'b': return '\b';
397         case 'f': return '\f';
398         case 'n': return '\n';
399         case 'r': return '\r';
400         case 't': return '\t';
401         case 'v': return '\v';
402         case 'x':
403                 return parse_hex_sequence();
404         case '0':
405         case '1':
406         case '2':
407         case '3':
408         case '4':
409         case '5':
410         case '6':
411         case '7':
412                 return parse_octal_sequence(ec);
413         case EOF:
414                 parse_error("reached end of file while parsing escape sequence");
415                 return EOF;
416         default:
417                 parse_error("unknown escape sequence");
418                 return EOF;
419         }
420 }
421
422 static void parse_string_literal(void)
423 {
424         const unsigned start_linenr = input.position.linenr;
425
426         eat('"');
427
428         int tc;
429         while(1) {
430                 switch(CC) {
431                 case '\\':
432                         if(resolve_escape_sequences) {
433                                 tc = parse_escape_sequence();
434                                 obstack_1grow(&symbol_obstack, (char) tc);
435                         } else {
436                                 obstack_1grow(&symbol_obstack, (char) CC);
437                                 next_char();
438                                 obstack_1grow(&symbol_obstack, (char) CC);
439                                 next_char();
440                         }
441                         break;
442
443                 case EOF: {
444                         source_position_t source_position;
445                         source_position.input_name = pp_token.source_position.input_name;
446                         source_position.linenr     = start_linenr;
447                         errorf(&source_position, "string has no end");
448                         pp_token.type = TP_ERROR;
449                         return;
450                 }
451
452                 case '"':
453                         next_char();
454                         goto end_of_string;
455
456                 default:
457                         obstack_1grow(&symbol_obstack, (char) CC);
458                         next_char();
459                         break;
460                 }
461         }
462
463 end_of_string:
464         /* add finishing 0 to the string */
465         obstack_1grow(&symbol_obstack, '\0');
466         const size_t      size   = (size_t)obstack_object_size(&symbol_obstack);
467         const char *const string = obstack_finish(&symbol_obstack);
468
469 #if 0 /* TODO hash */
470         /* check if there is already a copy of the string */
471         result = strset_insert(&stringset, string);
472         if(result != string) {
473                 obstack_free(&symbol_obstack, string);
474         }
475 #else
476         const char *const result = string;
477 #endif
478
479         pp_token.type           = TP_STRING_LITERAL;
480         pp_token.v.string.begin = result;
481         pp_token.v.string.size  = size;
482 }
483
484 static void parse_wide_character_constant(void)
485 {
486         eat('\'');
487
488         int found_char = 0;
489         while(1) {
490                 switch(CC) {
491                 case '\\':
492                         found_char = parse_escape_sequence();
493                         break;
494
495                 MATCH_NEWLINE(
496                         parse_error("newline while parsing character constant");
497                         break;
498                 )
499
500                 case '\'':
501                         next_char();
502                         goto end_of_wide_char_constant;
503
504                 case EOF:
505                         parse_error("EOF while parsing character constant");
506                         pp_token.type = TP_ERROR;
507                         return;
508
509                 default:
510                         if(found_char != 0) {
511                                 parse_error("more than 1 characters in character "
512                                             "constant");
513                                 goto end_of_wide_char_constant;
514                         } else {
515                                 found_char = CC;
516                                 next_char();
517                         }
518                         break;
519                 }
520         }
521
522 end_of_wide_char_constant:
523         pp_token.type       = TP_WIDE_CHARACTER_CONSTANT;
524         /* TODO... */
525 }
526
527 static void parse_wide_string_literal(void)
528 {
529         const unsigned start_linenr = input.position.linenr;
530
531         assert(CC == '"');
532         next_char();
533
534         while(1) {
535                 switch(CC) {
536                 case '\\': {
537                         wchar_rep_t tc = parse_escape_sequence();
538                         obstack_grow(&symbol_obstack, &tc, sizeof(tc));
539                         break;
540                 }
541
542                 case EOF: {
543                         source_position_t source_position;
544                         source_position.input_name = pp_token.source_position.input_name;
545                         source_position.linenr     = start_linenr;
546                         errorf(&source_position, "string has no end");
547                         pp_token.type = TP_ERROR;
548                         return;
549                 }
550
551                 case '"':
552                         next_char();
553                         goto end_of_string;
554
555                 default: {
556                         wchar_rep_t tc = CC;
557                         obstack_grow(&symbol_obstack, &tc, sizeof(tc));
558                         next_char();
559                         break;
560                 }
561                 }
562         }
563
564 end_of_string:;
565         /* add finishing 0 to the string */
566         static const wchar_rep_t nul = L'\0';
567         obstack_grow(&symbol_obstack, &nul, sizeof(nul));
568
569         const size_t size
570                 = (size_t)obstack_object_size(&symbol_obstack) / sizeof(wchar_rep_t);
571         const wchar_rep_t *const string = obstack_finish(&symbol_obstack);
572
573 #if 0 /* TODO hash */
574         /* check if there is already a copy of the string */
575         const wchar_rep_t *const result = strset_insert(&stringset, string);
576         if(result != string) {
577                 obstack_free(&symbol_obstack, string);
578         }
579 #else
580         const wchar_rep_t *const result = string;
581 #endif
582
583         pp_token.type                = TP_WIDE_STRING_LITERAL;
584         pp_token.v.wide_string.begin = result;
585         pp_token.v.wide_string.size  = size;
586 }
587
588 static void parse_character_constant(void)
589 {
590         const unsigned start_linenr = input.position.linenr;
591
592         eat('\'');
593
594         int tc;
595         while(1) {
596                 switch(CC) {
597                 case '\\':
598                         tc = parse_escape_sequence();
599                         obstack_1grow(&symbol_obstack, (char) tc);
600                         break;
601
602                 MATCH_NEWLINE(
603                         parse_error("newline while parsing character constant");
604                         break;
605                 )
606
607                 case EOF: {
608                         source_position_t source_position;
609                         source_position.input_name = pp_token.source_position.input_name;
610                         source_position.linenr     = start_linenr;
611                         errorf(&source_position, "EOF while parsing character constant");
612                         pp_token.type = TP_ERROR;
613                         return;
614                 }
615
616                 case '\'':
617                         next_char();
618                         goto end_of_char_constant;
619
620                 default:
621                         obstack_1grow(&symbol_obstack, (char) CC);
622                         next_char();
623                         break;
624
625                 }
626         }
627
628 end_of_char_constant:;
629         const size_t      size   = (size_t)obstack_object_size(&symbol_obstack);
630         const char *const string = obstack_finish(&symbol_obstack);
631
632         pp_token.type           = TP_CHARACTER_CONSTANT;
633         pp_token.v.string.begin = string;
634         pp_token.v.string.size  = size;
635 }
636
637 #define SYMBOL_CHARS_WITHOUT_E_P \
638         case 'a': \
639         case 'b': \
640         case 'c': \
641         case 'd': \
642         case 'f': \
643         case 'g': \
644         case 'h': \
645         case 'i': \
646         case 'j': \
647         case 'k': \
648         case 'l': \
649         case 'm': \
650         case 'n': \
651         case 'o': \
652         case 'q': \
653         case 'r': \
654         case 's': \
655         case 't': \
656         case 'u': \
657         case 'v': \
658         case 'w': \
659         case 'x': \
660         case 'y': \
661         case 'z': \
662         case 'A': \
663         case 'B': \
664         case 'C': \
665         case 'D': \
666         case 'F': \
667         case 'G': \
668         case 'H': \
669         case 'I': \
670         case 'J': \
671         case 'K': \
672         case 'L': \
673         case 'M': \
674         case 'N': \
675         case 'O': \
676         case 'Q': \
677         case 'R': \
678         case 'S': \
679         case 'T': \
680         case 'U': \
681         case 'V': \
682         case 'W': \
683         case 'X': \
684         case 'Y': \
685         case 'Z': \
686         case '_':
687
688 #define SYMBOL_CHARS \
689         SYMBOL_CHARS_WITHOUT_E_P \
690         case 'e': \
691         case 'p': \
692         case 'E': \
693         case 'P':
694
695 #define DIGITS \
696         case '0':  \
697         case '1':  \
698         case '2':  \
699         case '3':  \
700         case '4':  \
701         case '5':  \
702         case '6':  \
703         case '7':  \
704         case '8':  \
705         case '9':
706
707 /**
708  * returns next final token from a preprocessor macro expansion
709  */
710 static void expand_next(void)
711 {
712         assert(current_expansion != NULL);
713
714         pp_definition_t *definition = current_expansion;
715
716 restart:
717         if(definition->list_len == 0
718                         || definition->expand_pos >= definition->list_len) {
719                 /* we're finished with the current macro, move up 1 level in the
720                  * expansion stack */
721                 pp_definition_t *parent = definition->parent_expansion;
722                 definition->parent_expansion = NULL;
723                 definition->is_expanding     = false;
724
725                 /* it was the outermost expansion, parse normal pptoken */
726                 if(parent == NULL) {
727                         current_expansion = NULL;
728                         next_preprocessing_token();
729                         return;
730                 }
731                 definition        = parent;
732                 current_expansion = definition;
733                 goto restart;
734         }
735         pp_token = definition->replacement_list[definition->expand_pos];
736         ++definition->expand_pos;
737
738         if(pp_token.type != TP_IDENTIFIER)
739                 return;
740
741         /* if it was an identifier then we might need to expand again */
742         pp_definition_t *symbol_definition = pp_token.v.symbol->pp_definition;
743         if(symbol_definition != NULL && !symbol_definition->is_expanding) {
744                 symbol_definition->parent_expansion = definition;
745                 symbol_definition->expand_pos       = 0;
746                 symbol_definition->is_expanding     = true;
747                 definition                          = symbol_definition;
748                 current_expansion                   = definition;
749                 goto restart;
750         }
751 }
752
753 static void parse_symbol(void)
754 {
755         obstack_1grow(&symbol_obstack, (char) CC);
756         next_char();
757
758         while(1) {
759                 switch(CC) {
760                 DIGITS
761                 SYMBOL_CHARS
762                         obstack_1grow(&symbol_obstack, (char) CC);
763                         next_char();
764                         break;
765
766                 default:
767                         goto end_symbol;
768                 }
769         }
770
771 end_symbol:
772         obstack_1grow(&symbol_obstack, '\0');
773         char *string = obstack_finish(&symbol_obstack);
774
775         /* might be a wide string or character constant ( L"string"/L'c' ) */
776         if(CC == '"' && string[0] == 'L' && string[1] == '\0') {
777                 obstack_free(&symbol_obstack, string);
778                 parse_wide_string_literal();
779                 return;
780         } else if(CC == '\'' && string[0] == 'L' && string[1] == '\0') {
781                 obstack_free(&symbol_obstack, string);
782                 parse_wide_character_constant();
783                 return;
784         }
785
786         symbol_t *symbol = symbol_table_insert(string);
787
788         pp_token.type     = symbol->pp_ID;
789         pp_token.v.symbol = symbol;
790
791         /* we can free the memory from symbol obstack if we already had an entry in
792          * the symbol table */
793         if(symbol->string != string) {
794                 obstack_free(&symbol_obstack, string);
795         }
796
797         pp_definition_t *pp_definition = symbol->pp_definition;
798         if(do_expansions && pp_definition != NULL) {
799                 pp_definition->expand_pos   = 0;
800                 pp_definition->is_expanding = true,
801                 current_expansion           = pp_definition;
802                 expand_next();
803         }
804 }
805
806 static void parse_number(void)
807 {
808         obstack_1grow(&symbol_obstack, (char) CC);
809         next_char();
810
811         while(1) {
812                 switch(CC) {
813                 case '.':
814                 DIGITS
815                 SYMBOL_CHARS_WITHOUT_E_P
816                         obstack_1grow(&symbol_obstack, (char) CC);
817                         next_char();
818                         break;
819
820                 case 'e':
821                 case 'p':
822                 case 'E':
823                 case 'P':
824                         obstack_1grow(&symbol_obstack, (char) CC);
825                         next_char();
826                         if(CC == '+' || CC == '-') {
827                                 obstack_1grow(&symbol_obstack, (char) CC);
828                                 next_char();
829                         }
830                         break;
831
832                 default:
833                         goto end_number;
834                 }
835         }
836
837 end_number:
838         obstack_1grow(&symbol_obstack, '\0');
839         size_t  size   = obstack_object_size(&symbol_obstack);
840         char   *string = obstack_finish(&symbol_obstack);
841
842         pp_token.type           = TP_NUMBER;
843         pp_token.v.string.begin = string;
844         pp_token.v.string.size  = size;
845 }
846
847 static void skip_multiline_comment(void)
848 {
849         if(do_print_spaces)
850                 counted_spaces++;
851
852         unsigned start_linenr = input.position.linenr;
853         while(1) {
854                 switch(CC) {
855                 case '/':
856                         next_char();
857                         if (CC == '*') {
858                                 /* TODO: nested comment, warn here */
859                         }
860                         break;
861                 case '*':
862                         next_char();
863                         if(CC == '/') {
864                                 next_char();
865                                 return;
866                         }
867                         break;
868
869                 MATCH_NEWLINE(
870                         if(do_print_spaces) {
871                                 counted_newlines++;
872                                 counted_spaces = 0;
873                         }
874                         break;
875                 )
876
877                 case EOF: {
878                         source_position_t source_position;
879                         source_position.input_name = pp_token.source_position.input_name;
880                         source_position.linenr     = start_linenr;
881                         errorf(&source_position, "at end of file while looking for comment end");
882                         return;
883                 }
884
885                 default:
886                         next_char();
887                         break;
888                 }
889         }
890 }
891
892 static void skip_line_comment(void)
893 {
894         if(do_print_spaces)
895                 counted_spaces++;
896
897         while(1) {
898                 switch(CC) {
899                 case EOF:
900                         return;
901
902                 case '\n':
903                 case '\r':
904                         return;
905
906                 default:
907                         next_char();
908                         break;
909                 }
910         }
911 }
912
913
914
915 #define MAYBE_PROLOG                                       \
916                         next_char();                                   \
917                         while(1) {                                     \
918                                 switch(CC) {
919
920 #define MAYBE(ch, set_type)                                \
921                                 case ch:                                   \
922                                         next_char();                           \
923                                         pp_token.type = set_type;              \
924                                         return;
925
926 #define ELSE_CODE(code)                                    \
927                                 default:                                   \
928                                         code;                                  \
929                                 }                                          \
930                         } /* end of while(1) */                        \
931                         break;
932
933 #define ELSE(set_type)                                     \
934                 ELSE_CODE(                                         \
935                         pp_token.type = set_type;                      \
936                         return;                                        \
937                 )
938
939 static void next_preprocessing_token(void)
940 {
941         if(current_expansion != NULL) {
942                 expand_next();
943                 return;
944         }
945
946         pp_token.source_position = input.position;
947
948 restart:
949         switch(CC) {
950         case ' ':
951         case '\t':
952                 if(do_print_spaces)
953                         counted_spaces++;
954                 next_char();
955                 goto restart;
956
957         MATCH_NEWLINE(
958                 counted_newlines++;
959                 counted_spaces = 0;
960                 pp_token.type = '\n';
961                 return;
962         )
963
964         SYMBOL_CHARS
965                 parse_symbol();
966                 return;
967
968         DIGITS
969                 parse_number();
970                 return;
971
972         case '"':
973                 parse_string_literal();
974                 return;
975
976         case '\'':
977                 parse_character_constant();
978                 return;
979
980         case '.':
981                 MAYBE_PROLOG
982                         case '0':
983                         case '1':
984                         case '2':
985                         case '3':
986                         case '4':
987                         case '5':
988                         case '6':
989                         case '7':
990                         case '8':
991                         case '9':
992                                 put_back(CC);
993                                 CC = '.';
994                                 parse_number();
995                                 return;
996
997                         case '.':
998                                 MAYBE_PROLOG
999                                 MAYBE('.', TP_DOTDOTDOT)
1000                                 ELSE_CODE(
1001                                         put_back(CC);
1002                                         CC = '.';
1003                                         pp_token.type = '.';
1004                                         return;
1005                                 )
1006                 ELSE('.')
1007         case '&':
1008                 MAYBE_PROLOG
1009                 MAYBE('&', TP_ANDAND)
1010                 MAYBE('=', TP_ANDEQUAL)
1011                 ELSE('&')
1012         case '*':
1013                 MAYBE_PROLOG
1014                 MAYBE('=', TP_ASTERISKEQUAL)
1015                 ELSE('*')
1016         case '+':
1017                 MAYBE_PROLOG
1018                 MAYBE('+', TP_PLUSPLUS)
1019                 MAYBE('=', TP_PLUSEQUAL)
1020                 ELSE('+')
1021         case '-':
1022                 MAYBE_PROLOG
1023                 MAYBE('>', TP_MINUSGREATER)
1024                 MAYBE('-', TP_MINUSMINUS)
1025                 MAYBE('=', TP_MINUSEQUAL)
1026                 ELSE('-')
1027         case '!':
1028                 MAYBE_PROLOG
1029                 MAYBE('=', TP_EXCLAMATIONMARKEQUAL)
1030                 ELSE('!')
1031         case '/':
1032                 MAYBE_PROLOG
1033                 MAYBE('=', TP_SLASHEQUAL)
1034                         case '*':
1035                                 next_char();
1036                                 skip_multiline_comment();
1037                                 goto restart;
1038                         case '/':
1039                                 next_char();
1040                                 skip_line_comment();
1041                                 goto restart;
1042                 ELSE('/')
1043         case '%':
1044                 MAYBE_PROLOG
1045                 MAYBE('>', '}')
1046                 MAYBE('=', TP_PERCENTEQUAL)
1047                         case ':':
1048                                 MAYBE_PROLOG
1049                                         case '%':
1050                                                 MAYBE_PROLOG
1051                                                 MAYBE(':', TP_HASHHASH)
1052                                                 ELSE_CODE(
1053                                                         put_back(CC);
1054                                                         CC = '%';
1055                                                         pp_token.type = '#';
1056                                                         return;
1057                                                 )
1058                                 ELSE('#')
1059                 ELSE('%')
1060         case '<':
1061                 MAYBE_PROLOG
1062                 MAYBE(':', '[')
1063                 MAYBE('%', '{')
1064                 MAYBE('=', TP_LESSEQUAL)
1065                         case '<':
1066                                 MAYBE_PROLOG
1067                                 MAYBE('=', TP_LESSLESSEQUAL)
1068                                 ELSE(TP_LESSLESS)
1069                 ELSE('<')
1070         case '>':
1071                 MAYBE_PROLOG
1072                 MAYBE('=', TP_GREATEREQUAL)
1073                         case '>':
1074                                 MAYBE_PROLOG
1075                                 MAYBE('=', TP_GREATERGREATEREQUAL)
1076                                 ELSE(TP_GREATERGREATER)
1077                 ELSE('>')
1078         case '^':
1079                 MAYBE_PROLOG
1080                 MAYBE('=', TP_CARETEQUAL)
1081                 ELSE('^')
1082         case '|':
1083                 MAYBE_PROLOG
1084                 MAYBE('=', TP_PIPEEQUAL)
1085                 MAYBE('|', TP_PIPEPIPE)
1086                 ELSE('|')
1087         case ':':
1088                 MAYBE_PROLOG
1089                 MAYBE('>', ']')
1090                 ELSE(':')
1091         case '=':
1092                 MAYBE_PROLOG
1093                 MAYBE('=', TP_EQUALEQUAL)
1094                 ELSE('=')
1095         case '#':
1096                 MAYBE_PROLOG
1097                 MAYBE('#', TP_HASHHASH)
1098                 ELSE('#')
1099
1100         case '?':
1101         case '[':
1102         case ']':
1103         case '(':
1104         case ')':
1105         case '{':
1106         case '}':
1107         case '~':
1108         case ';':
1109         case ',':
1110         case '\\':
1111                 pp_token.type = CC;
1112                 next_char();
1113                 return;
1114
1115         case EOF:
1116                 if (input_stack != NULL) {
1117                         close_input();
1118                         pop_restore_input();
1119                         counted_newlines = 0;
1120                         counted_spaces   = 0;
1121                         /* hack to output correct line number */
1122                         print_line_directive(&input.position, "2");
1123                         next_preprocessing_token();
1124                 } else {
1125                         pp_token.type = TP_EOF;
1126                 }
1127                 return;
1128
1129         default:
1130                 next_char();
1131                 errorf(&pp_token.source_position, "unknown character '%c' found\n", CC);
1132                 pp_token.type = TP_ERROR;
1133                 return;
1134         }
1135 }
1136
1137 static void print_quoted_string(const char *const string)
1138 {
1139         fputc('"', out);
1140         for (const char *c = string; *c != 0; ++c) {
1141                 switch(*c) {
1142                 case '"': fputs("\\\"", out); break;
1143                 case '\\':  fputs("\\\\", out); break;
1144                 case '\a':  fputs("\\a", out); break;
1145                 case '\b':  fputs("\\b", out); break;
1146                 case '\f':  fputs("\\f", out); break;
1147                 case '\n':  fputs("\\n", out); break;
1148                 case '\r':  fputs("\\r", out); break;
1149                 case '\t':  fputs("\\t", out); break;
1150                 case '\v':  fputs("\\v", out); break;
1151                 case '\?':  fputs("\\?", out); break;
1152                 default:
1153                         if(!isprint(*c)) {
1154                                 fprintf(out, "\\%03o", *c);
1155                                 break;
1156                         }
1157                         fputc(*c, out);
1158                         break;
1159                 }
1160         }
1161         fputc('"', out);
1162 }
1163
1164 static void print_line_directive(const source_position_t *pos, const char *add)
1165 {
1166         fprintf(out, "# %d ", pos->linenr);
1167         print_quoted_string(pos->input_name);
1168         if (add != NULL) {
1169                 fputc(' ', out);
1170                 fputs(add, out);
1171         }
1172         fputc('\n', out);
1173
1174         printed_input_name = pos->input_name;
1175 }
1176
1177 static void print_spaces(void)
1178 {
1179         if (counted_newlines >= 8) {
1180                 if (input.had_non_space) {
1181                         fputc('\n', out);
1182                 }
1183                 print_line_directive(&pp_token.source_position, NULL);
1184                 counted_newlines = 0;
1185         } else {
1186                 for (unsigned i = 0; i < counted_newlines; ++i)
1187                         fputc('\n', out);
1188                 counted_newlines = 0;
1189         }
1190         for (unsigned i = 0; i < counted_spaces; ++i)
1191                 fputc(' ', out);
1192         counted_spaces = 0;
1193 }
1194
1195 static void emit_pp_token(void)
1196 {
1197         if (skip_mode)
1198                 return;
1199
1200         if (pp_token.type != '\n') {
1201                 print_spaces();
1202                 input.had_non_space = true;
1203         }
1204
1205         switch(pp_token.type) {
1206         case TP_IDENTIFIER:
1207                 fputs(pp_token.v.symbol->string, out);
1208                 break;
1209         case TP_NUMBER:
1210                 fputs(pp_token.v.string.begin, out);
1211                 break;
1212         case TP_STRING_LITERAL:
1213                 fputc('"', out);
1214                 fputs(pp_token.v.string.begin, out);
1215                 fputc('"', out);
1216                 break;
1217         case '\n':
1218                 break;
1219         default:
1220                 print_pp_token_type(out, pp_token.type);
1221                 break;
1222         }
1223 }
1224
1225 static void eat_pp(preprocessor_token_type_t type)
1226 {
1227         (void) type;
1228         assert(pp_token.type == type);
1229         next_preprocessing_token();
1230 }
1231
1232 static void eat_pp_directive(void)
1233 {
1234         while(pp_token.type != '\n' && pp_token.type != TP_EOF) {
1235                 next_preprocessing_token();
1236         }
1237 }
1238
1239 static bool strings_equal(const string_t *string1, const string_t *string2)
1240 {
1241         size_t size = string1->size;
1242         if(size != string2->size)
1243                 return false;
1244
1245         const char *c1 = string1->begin;
1246         const char *c2 = string2->begin;
1247         for(size_t i = 0; i < size; ++i, ++c1, ++c2) {
1248                 if(*c1 != *c2)
1249                         return false;
1250         }
1251         return true;
1252 }
1253
1254 static bool wide_strings_equal(const wide_string_t *string1,
1255                                const wide_string_t *string2)
1256 {
1257         size_t size = string1->size;
1258         if(size != string2->size)
1259                 return false;
1260
1261         const wchar_rep_t *c1 = string1->begin;
1262         const wchar_rep_t *c2 = string2->begin;
1263         for(size_t i = 0; i < size; ++i, ++c1, ++c2) {
1264                 if(*c1 != *c2)
1265                         return false;
1266         }
1267         return true;
1268 }
1269
1270 static bool pp_tokens_equal(const token_t *token1, const token_t *token2)
1271 {
1272         if(token1->type != token2->type)
1273                 return false;
1274
1275         switch(token1->type) {
1276         case TP_HEADERNAME:
1277                 /* TODO */
1278                 return false;
1279         case TP_IDENTIFIER:
1280                 return token1->v.symbol == token2->v.symbol;
1281         case TP_NUMBER:
1282         case TP_CHARACTER_CONSTANT:
1283         case TP_STRING_LITERAL:
1284                 return strings_equal(&token1->v.string, &token2->v.string);
1285
1286         case TP_WIDE_CHARACTER_CONSTANT:
1287         case TP_WIDE_STRING_LITERAL:
1288                 return wide_strings_equal(&token1->v.wide_string,
1289                                           &token2->v.wide_string);
1290         default:
1291                 return true;
1292         }
1293 }
1294
1295 static bool pp_definitions_equal(const pp_definition_t *definition1,
1296                                  const pp_definition_t *definition2)
1297 {
1298         if(definition1->list_len != definition2->list_len)
1299                 return false;
1300
1301         size_t         len = definition1->list_len;
1302         const token_t *t1  = definition1->replacement_list;
1303         const token_t *t2  = definition2->replacement_list;
1304         for(size_t i = 0; i < len; ++i, ++t1, ++t2) {
1305                 if(!pp_tokens_equal(t1, t2))
1306                         return false;
1307         }
1308         return true;
1309 }
1310
1311 static void parse_define_directive(void)
1312 {
1313         eat_pp(TP_define);
1314         assert(obstack_object_size(&pp_obstack) == 0);
1315
1316         if (pp_token.type != TP_IDENTIFIER) {
1317                 errorf(&pp_token.source_position,
1318                        "expected identifier after #define, got '%t'", &pp_token);
1319                 goto error_out;
1320         }
1321         symbol_t *symbol = pp_token.v.symbol;
1322
1323         pp_definition_t *new_definition
1324                 = obstack_alloc(&pp_obstack, sizeof(new_definition[0]));
1325         memset(new_definition, 0, sizeof(new_definition[0]));
1326         new_definition->source_position = input.position;
1327
1328         /* this is probably the only place where spaces are significant in the
1329          * lexer (except for the fact that they separate tokens). #define b(x)
1330          * is something else than #define b (x) */
1331         if (CC == '(') {
1332                 /* eat the '(' */
1333                 next_preprocessing_token();
1334                 /* get next token after '(' */
1335                 next_preprocessing_token();
1336
1337                 while (true) {
1338                         switch (pp_token.type) {
1339                         case TP_DOTDOTDOT:
1340                                 new_definition->is_variadic = true;
1341                                 next_preprocessing_token();
1342                                 if (pp_token.type != ')') {
1343                                         errorf(&input.position,
1344                                                         "'...' not at end of macro argument list");
1345                                         goto error_out;
1346                                 }
1347                                 break;
1348                         case TP_IDENTIFIER:
1349                                 obstack_ptr_grow(&pp_obstack, pp_token.v.symbol);
1350                                 next_preprocessing_token();
1351
1352                                 if (pp_token.type == ',') {
1353                                         next_preprocessing_token();
1354                                         break;
1355                                 }
1356
1357                                 if (pp_token.type != ')') {
1358                                         errorf(&pp_token.source_position,
1359                                                "expected ',' or ')' after identifier, got '%t'",
1360                                                &pp_token);
1361                                         goto error_out;
1362                                 }
1363                                 break;
1364                         case ')':
1365                                 goto finish_argument_list;
1366                         default:
1367                                 errorf(&pp_token.source_position,
1368                                        "expected identifier, '...' or ')' in #define argument list, got '%t'",
1369                                        &pp_token);
1370                                 goto error_out;
1371                         }
1372                 }
1373
1374         finish_argument_list:
1375                 new_definition->has_arguments = true;
1376                 new_definition->n_arguments
1377                         = obstack_object_size(&pp_obstack) / sizeof(new_definition->arguments[0]);
1378                 new_definition->arguments = obstack_finish(&pp_obstack);
1379         } else {
1380                 next_preprocessing_token();
1381         }
1382
1383         /* construct a new pp_definition on the obstack */
1384         assert(obstack_object_size(&pp_obstack) == 0);
1385         size_t list_len = 0;
1386         while (pp_token.type != '\n' && pp_token.type != TP_EOF) {
1387                 obstack_grow(&pp_obstack, &pp_token, sizeof(pp_token));
1388                 ++list_len;
1389                 next_preprocessing_token();
1390         }
1391
1392         new_definition->list_len         = list_len;
1393         new_definition->replacement_list = obstack_finish(&pp_obstack);
1394
1395         pp_definition_t *old_definition = symbol->pp_definition;
1396         if (old_definition != NULL) {
1397                 if (!pp_definitions_equal(old_definition, new_definition)) {
1398                         warningf(&input.position, "multiple definition of macro '%Y' (first defined %P)",
1399                                  symbol, &old_definition->source_position);
1400                 } else {
1401                         /* reuse the old definition */
1402                         obstack_free(&pp_obstack, new_definition);
1403                         new_definition = old_definition;
1404                 }
1405         }
1406
1407         symbol->pp_definition = new_definition;
1408         return;
1409
1410 error_out:
1411         if (obstack_object_size(&pp_obstack) > 0) {
1412                 char *ptr = obstack_finish(&pp_obstack);
1413                 obstack_free(&pp_obstack, ptr);
1414         }
1415         eat_pp_directive();
1416 }
1417
1418 static void parse_undef_directive(void)
1419 {
1420         eat_pp(TP_undef);
1421
1422         if(pp_token.type != TP_IDENTIFIER) {
1423                 errorf(&input.position,
1424                        "expected identifier after #undef, got '%t'", &pp_token);
1425                 eat_pp_directive();
1426                 return;
1427         }
1428
1429         symbol_t *symbol = pp_token.v.symbol;
1430         symbol->pp_definition = NULL;
1431         next_preprocessing_token();
1432
1433         if(pp_token.type != '\n') {
1434                 warningf(&input.position, "extra tokens at end of #undef directive");
1435         }
1436         /* eat until '\n' */
1437         eat_pp_directive();
1438 }
1439
1440 /* skip spaces advancing at the start of the next preprocessing token */
1441 static void skip_spaces(void)
1442 {
1443         while (true) {
1444                 switch (CC) {
1445                 case ' ':
1446                 case '\t':
1447                         if(do_print_spaces)
1448                                 counted_spaces++;
1449                         next_char();
1450                         continue;
1451                 case '/':
1452                         next_char();
1453                         if (CC == '/') {
1454                                 next_char();
1455                                 skip_line_comment();
1456                                 continue;
1457                         } else if (CC == '*') {
1458                                 next_char();
1459                                 skip_multiline_comment();
1460                                 continue;
1461                         } else {
1462                                 put_back(CC);
1463                                 CC = '/';
1464                         }
1465                         return;
1466                 default:
1467                         return;
1468                 }
1469         }
1470 }
1471
1472 static const char *parse_headername(void)
1473 {
1474         /* behind an #include we can have the special headername lexems.
1475          * They're only allowed behind an #include so they're not recognized
1476          * by the normal next_preprocessing_token. We handle them as a special
1477          * exception here */
1478
1479         /* skip spaces so we reach start of next preprocessing token */
1480         skip_spaces();
1481
1482         assert(obstack_object_size(&input_obstack) == 0);
1483
1484         /* check wether we have a "... or <... headername */
1485         switch (CC) {
1486         case '<':
1487                 /* for now until we have proper searchpath handling */
1488                 obstack_1grow(&input_obstack, '.');
1489                 obstack_1grow(&input_obstack, '/');
1490
1491                 next_char();
1492                 while (true) {
1493                         switch (CC) {
1494                         case EOF:
1495                                 /* fallthrough */
1496                         MATCH_NEWLINE(
1497                                 parse_error("header name without closing '>'");
1498                                 return NULL;
1499                         )
1500                         case '>':
1501                                 next_char();
1502                                 goto finished_headername;
1503                         }
1504                         obstack_1grow(&input_obstack, (char) CC);
1505                         next_char();
1506                 }
1507                 /* we should never be here */
1508
1509         case '"':
1510                 /* for now until we have proper searchpath handling */
1511                 obstack_1grow(&input_obstack, '.');
1512                 obstack_1grow(&input_obstack, '/');
1513
1514                 next_char();
1515                 while (true) {
1516                         switch (CC) {
1517                         case EOF:
1518                                 /* fallthrough */
1519                         MATCH_NEWLINE(
1520                                 parse_error("header name without closing '>'");
1521                                 return NULL;
1522                         )
1523                         case '"':
1524                                 next_char();
1525                                 goto finished_headername;
1526                         }
1527                         obstack_1grow(&input_obstack, (char) CC);
1528                         next_char();
1529                 }
1530                 /* we should never be here */
1531
1532         default:
1533                 /* TODO: do normale pp_token parsing and concatenate results */
1534                 panic("pp_token concat include not implemented yet");
1535         }
1536
1537 finished_headername:
1538         obstack_1grow(&input_obstack, '\0');
1539         char *headername = obstack_finish(&input_obstack);
1540
1541         /* TODO: iterate search-path to find the file */
1542
1543         next_preprocessing_token();
1544
1545         return headername;
1546 }
1547
1548 static bool parse_include_directive(void)
1549 {
1550         /* don't eat the TP_include here!
1551          * we need an alternative parsing for the next token */
1552
1553         print_spaces();
1554
1555         const char *headername = parse_headername();
1556         if (headername == NULL) {
1557                 eat_pp_directive();
1558                 return false;
1559         }
1560
1561         if (pp_token.type != '\n' && pp_token.type != TP_EOF) {
1562                 warningf(&pp_token.source_position,
1563                          "extra tokens at end of #include directive");
1564                 eat_pp_directive();
1565         }
1566
1567         if (n_inputs > INCLUDE_LIMIT) {
1568                 errorf(&pp_token.source_position, "#include nested too deeply");
1569                 /* eat \n or EOF */
1570                 next_preprocessing_token();
1571                 return false;
1572         }
1573
1574         /* we have to reenable space counting and macro expansion here,
1575          * because it is still disabled in directive parsing,
1576          * but we will trigger a preprocessing token reading of the new file
1577          * now and need expansions/space counting */
1578         do_print_spaces = true;
1579         do_expansions   = true;
1580
1581         /* switch inputs */
1582         push_input();
1583         bool res = open_input(headername);
1584         if (!res) {
1585                 errorf(&pp_token.source_position,
1586                        "failed including '%s': %s", headername, strerror(errno));
1587                 pop_restore_input();
1588                 return false;
1589         }
1590
1591         return true;
1592 }
1593
1594 static pp_conditional_t *push_conditional(void)
1595 {
1596         pp_conditional_t *conditional
1597                 = obstack_alloc(&pp_obstack, sizeof(*conditional));
1598         memset(conditional, 0, sizeof(*conditional));
1599
1600         conditional->parent = conditional_stack;
1601         conditional_stack   = conditional;
1602
1603         return conditional;
1604 }
1605
1606 static void pop_conditional(void)
1607 {
1608         assert(conditional_stack != NULL);
1609         conditional_stack = conditional_stack->parent;
1610 }
1611
1612 static void check_unclosed_conditionals(void)
1613 {
1614         while (conditional_stack != NULL) {
1615                 pp_conditional_t *conditional = conditional_stack;
1616
1617                 if (conditional->in_else) {
1618                         errorf(&conditional->source_position, "unterminated #else");
1619                 } else {
1620                         errorf(&conditional->source_position, "unterminated condition");
1621                 }
1622                 pop_conditional();
1623         }
1624 }
1625
1626 static void parse_ifdef_ifndef_directive(void)
1627 {
1628         bool is_ifndef = (pp_token.type == TP_ifndef);
1629         bool condition;
1630         next_preprocessing_token();
1631
1632         if (skip_mode) {
1633                 eat_pp_directive();
1634                 pp_conditional_t *conditional = push_conditional();
1635                 conditional->source_position  = pp_token.source_position;
1636                 conditional->skip             = true;
1637                 return;
1638         }
1639
1640         if (pp_token.type != TP_IDENTIFIER) {
1641                 errorf(&pp_token.source_position,
1642                        "expected identifier after #%s, got '%t'",
1643                        is_ifndef ? "ifndef" : "ifdef", &pp_token);
1644                 eat_pp_directive();
1645
1646                 /* just take the true case in the hope to avoid further errors */
1647                 condition = true;
1648         } else {
1649                 symbol_t        *symbol        = pp_token.v.symbol;
1650                 pp_definition_t *pp_definition = symbol->pp_definition;
1651                 next_preprocessing_token();
1652
1653                 if (pp_token.type != '\n') {
1654                         errorf(&pp_token.source_position,
1655                                "extra tokens at end of #%s",
1656                                is_ifndef ? "ifndef" : "ifdef");
1657                         eat_pp_directive();
1658                 }
1659
1660                 /* evaluate wether we are in true or false case */
1661                 condition = is_ifndef ? pp_definition == NULL : pp_definition != NULL;
1662         }
1663
1664         pp_conditional_t *conditional = push_conditional();
1665         conditional->source_position  = pp_token.source_position;
1666         conditional->condition        = condition;
1667
1668         if (!condition) {
1669                 skip_mode = true;
1670         }
1671 }
1672
1673 static void parse_else_directive(void)
1674 {
1675         eat_pp(TP_else);
1676
1677         if (pp_token.type != '\n') {
1678                 if (!skip_mode) {
1679                         warningf(&pp_token.source_position, "extra tokens at end of #else");
1680                 }
1681                 eat_pp_directive();
1682         }
1683
1684         pp_conditional_t *conditional = conditional_stack;
1685         if (conditional == NULL) {
1686                 errorf(&pp_token.source_position, "#else without prior #if");
1687                 return;
1688         }
1689
1690         if (conditional->in_else) {
1691                 errorf(&pp_token.source_position,
1692                        "#else after #else (condition started %P)",
1693                        conditional->source_position);
1694                 skip_mode = true;
1695                 return;
1696         }
1697
1698         conditional->in_else = true;
1699         if (!conditional->skip) {
1700                 skip_mode = conditional->condition;
1701         }
1702         conditional->source_position = pp_token.source_position;
1703 }
1704
1705 static void parse_endif_directive(void)
1706 {
1707         eat_pp(TP_endif);
1708
1709         if (pp_token.type != '\n') {
1710                 if (!skip_mode) {
1711                         warningf(&pp_token.source_position,
1712                                  "extra tokens at end of #endif");
1713                 }
1714                 eat_pp_directive();
1715         }
1716
1717         pp_conditional_t *conditional = conditional_stack;
1718         if (conditional == NULL) {
1719                 errorf(&pp_token.source_position, "#endif without prior #if");
1720                 return;
1721         }
1722
1723         if (!conditional->skip) {
1724                 skip_mode = false;
1725         }
1726         pop_conditional();
1727 }
1728
1729 static void parse_preprocessing_directive(void)
1730 {
1731         do_print_spaces = false;
1732         do_expansions   = false;
1733         eat_pp('#');
1734
1735         if (skip_mode) {
1736                 switch(pp_token.type) {
1737                 case TP_ifdef:
1738                 case TP_ifndef:
1739                         parse_ifdef_ifndef_directive();
1740                         break;
1741                 case TP_else:
1742                         parse_else_directive();
1743                         break;
1744                 case TP_endif:
1745                         parse_endif_directive();
1746                         break;
1747                 default:
1748                         eat_pp_directive();
1749                         break;
1750                 }
1751         } else {
1752                 switch(pp_token.type) {
1753                 case TP_define:
1754                         parse_define_directive();
1755                         break;
1756                 case TP_undef:
1757                         parse_undef_directive();
1758                         break;
1759                 case TP_ifdef:
1760                 case TP_ifndef:
1761                         parse_ifdef_ifndef_directive();
1762                         break;
1763                 case TP_else:
1764                         parse_else_directive();
1765                         break;
1766                 case TP_endif:
1767                         parse_endif_directive();
1768                         break;
1769                 case TP_include: {
1770                         bool in_new_source = parse_include_directive();
1771                         /* no need to do anything if source file switched */
1772                         if (in_new_source)
1773                                 return;
1774                         break;
1775                 }
1776                 case '\n':
1777                         /* the nop directive */
1778                         break;
1779                 default:
1780                         errorf(&pp_token.source_position,
1781                                    "invalid preprocessing directive #%t", &pp_token);
1782                         eat_pp_directive();
1783                         break;
1784                 }
1785         }
1786
1787         do_print_spaces = true;
1788         do_expansions   = true;
1789
1790         /* eat '\n' */
1791         assert(pp_token.type == '\n' || pp_token.type == TP_EOF);
1792         next_preprocessing_token();
1793 }
1794
1795 #define GCC_COMPAT_MODE
1796
1797 int pptest_main(int argc, char **argv);
1798 int pptest_main(int argc, char **argv)
1799 {
1800         init_symbol_table();
1801         init_tokens();
1802
1803         obstack_init(&pp_obstack);
1804         obstack_init(&input_obstack);
1805
1806         const char *filename = "t.c";
1807         if (argc > 1)
1808                 filename = argv[1];
1809
1810         out = stdout;
1811
1812 #ifdef GCC_COMPAT_MODE
1813         /* this is here so we can directly compare "gcc -E" output and our output */
1814         fprintf(out, "# 1 \"%s\"\n", filename);
1815         fputs("# 1 \"<built-in>\"\n", out);
1816         fputs("# 1 \"<command-line>\"\n", out);
1817 #endif
1818
1819         bool ok = open_input(filename);
1820         assert(ok);
1821
1822         while(true) {
1823                 /* we're at a line begin */
1824                 if(pp_token.type == '#') {
1825                         parse_preprocessing_directive();
1826                 } else {
1827                         /* parse+emit a line */
1828                         while(pp_token.type != '\n') {
1829                                 if(pp_token.type == TP_EOF)
1830                                         goto end_of_main_loop;
1831                                 emit_pp_token();
1832                                 next_preprocessing_token();
1833                         }
1834                         emit_pp_token();
1835                         next_preprocessing_token();
1836                 }
1837         }
1838 end_of_main_loop:
1839
1840         check_unclosed_conditionals();
1841         close_input();
1842
1843         obstack_free(&input_obstack, NULL);
1844         obstack_free(&pp_obstack, NULL);
1845
1846         exit_tokens();
1847         exit_symbol_table();
1848
1849         return 0;
1850 }