]> git.sur5r.net Git - cc65/blob - src/cc65/preproc.c
Ignore unknown preprocessor directives in an #if group that is excluded.
[cc65] / src / cc65 / preproc.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  preproc.c                                */
4 /*                                                                           */
5 /*                              cc65 preprocessor                            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2009, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
12 /* EMail:         uz@cc65.org                                                */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <errno.h>
40
41 /* common */
42 #include "chartype.h"
43 #include "check.h"
44 #include "inline.h"
45 #include "print.h"
46 #include "xmalloc.h"
47
48 /* cc65 */
49 #include "codegen.h"
50 #include "error.h"
51 #include "expr.h"
52 #include "global.h"
53 #include "ident.h"
54 #include "incpath.h"
55 #include "input.h"
56 #include "lineinfo.h"
57 #include "macrotab.h"
58 #include "preproc.h"
59 #include "scanner.h"
60 #include "standard.h"
61
62
63
64 /*****************************************************************************/
65 /*                                   Data                                    */
66 /*****************************************************************************/
67
68
69
70 /* Set when the preprocessor calls expr() recursively */
71 unsigned char Preprocessing = 0;
72
73 /* Management data for #if */
74 #define MAX_IFS         64
75 #define IFCOND_NONE     0x00U
76 #define IFCOND_SKIP     0x01U
77 #define IFCOND_ELSE     0x02U
78 #define IFCOND_NEEDTERM 0x04U
79 static unsigned char IfStack[MAX_IFS];
80 static int           IfIndex = -1;
81
82 /* Buffer for macro expansion */
83 static StrBuf* MLine;
84
85 /* Structure used when expanding macros */
86 typedef struct MacroExp MacroExp;
87 struct MacroExp {
88     Collection  ActualArgs;     /* Actual arguments */
89     StrBuf      Replacement;    /* Replacement with arguments substituted */
90     Macro*      M;              /* The macro we're handling */
91 };
92
93
94
95 /*****************************************************************************/
96 /*                                 Forwards                                  */
97 /*****************************************************************************/
98
99
100
101 static unsigned Pass1 (StrBuf* Source, StrBuf* Target);
102 /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments
103  * and the "defined" operator.
104  */
105
106 static void MacroReplacement (StrBuf* Source, StrBuf* Target);
107 /* Perform macro replacement. */
108
109
110
111 /*****************************************************************************/
112 /*                   Low level preprocessor token handling                   */
113 /*****************************************************************************/
114
115
116
117 /* Types of preprocessor tokens */
118 typedef enum {
119     PP_ILLEGAL  = -1,
120     PP_DEFINE,
121     PP_ELIF,
122     PP_ELSE,
123     PP_ENDIF,
124     PP_ERROR,
125     PP_IF,
126     PP_IFDEF,
127     PP_IFNDEF,
128     PP_INCLUDE,
129     PP_LINE,
130     PP_PRAGMA,
131     PP_UNDEF,
132     PP_WARNING,
133 } pptoken_t;
134
135
136
137 /* Preprocessor keyword to token mapping table */
138 static const struct PPToken {
139     const char* Key;            /* Keyword */
140     pptoken_t   Tok;            /* Token */
141 } PPTokens[] = {
142     {   "define",       PP_DEFINE       },
143     {   "elif",         PP_ELIF         },
144     {   "else",         PP_ELSE         },
145     {   "endif",        PP_ENDIF        },
146     {   "error",        PP_ERROR        },
147     {   "if",           PP_IF           },
148     {   "ifdef",        PP_IFDEF        },
149     {   "ifndef",       PP_IFNDEF       },
150     {   "include",      PP_INCLUDE      },
151     {   "line",         PP_LINE         },
152     {   "pragma",       PP_PRAGMA       },
153     {   "undef",        PP_UNDEF        },
154     {   "warning",      PP_WARNING      },
155 };
156
157 /* Number of preprocessor tokens */
158 #define PPTOKEN_COUNT   (sizeof(PPTokens) / sizeof(PPTokens[0]))
159
160
161
162 static int CmpToken (const void* Key, const void* Elem)
163 /* Compare function for bsearch */
164 {
165     return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
166 }
167
168
169
170 static pptoken_t FindPPToken (const char* Ident)
171 /* Find a preprocessor token and return it. Return PP_ILLEGAL if the identifier
172  * is not a valid preprocessor token.
173  */
174 {
175     struct PPToken* P;
176     P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
177     return P? P->Tok : PP_ILLEGAL;
178 }
179
180
181
182 /*****************************************************************************/
183 /*                              struct MacroExp                              */
184 /*****************************************************************************/
185
186
187
188 static MacroExp* InitMacroExp (MacroExp* E, Macro* M)
189 /* Initialize a MacroExp structure */
190 {
191     InitCollection (&E->ActualArgs);
192     SB_Init (&E->Replacement);
193     E->M = M;
194     return E;
195 }
196
197
198
199 static void DoneMacroExp (MacroExp* E)
200 /* Cleanup after use of a MacroExp structure */
201 {
202     unsigned I;
203
204     /* Delete the list with actual arguments */
205     for (I = 0; I < CollCount (&E->ActualArgs); ++I) {
206         FreeStrBuf (CollAtUnchecked (&E->ActualArgs, I));
207     }
208     DoneCollection (&E->ActualArgs);
209     SB_Done (&E->Replacement);
210 }
211
212
213
214 static void ME_AppendActual (MacroExp* E, StrBuf* Arg)
215 /* Add a copy of Arg to the list of actual macro arguments.
216  * NOTE: This function will clear Arg!
217  */
218 {
219     /* Create a new string buffer */
220     StrBuf* A = NewStrBuf ();
221
222     /* Move the contents of Arg to A */
223     SB_Move (A, Arg);
224
225     /* Add A to the actual arguments */
226     CollAppend (&E->ActualArgs, A);
227 }
228
229
230
231 static StrBuf* ME_GetActual (MacroExp* E, unsigned Index)
232 /* Return an actual macro argument with the given index */
233 {
234     return CollAt (&E->ActualArgs, Index);
235 }
236
237
238
239 static int ME_ArgIsVariadic (const MacroExp* E)
240 /* Return true if the next actual argument we will add is a variadic one */
241 {
242     return (E->M->Variadic &&
243             E->M->ArgCount == (int) CollCount (&E->ActualArgs) + 1);
244 }
245
246
247
248 /*****************************************************************************/
249 /*                                   Code                                    */
250 /*****************************************************************************/
251
252
253
254 static void Stringize (StrBuf* Source, StrBuf* Target)
255 /* Stringize the given string: Add double quotes at start and end and preceed
256  * each occurance of " and \ by a backslash.
257  */
258 {
259     char C;
260
261     /* Add a starting quote */
262     SB_AppendChar (Target, '\"');
263
264     /* Replace any characters inside the string may not be part of a string
265      * unescaped.
266      */
267     while ((C = SB_Get (Source)) != '\0') {
268         switch (C) {
269             case '\"':
270             case '\\':
271                 SB_AppendChar (Target, '\\');
272             /* FALLTHROUGH */
273             default:
274                 SB_AppendChar (Target, C);
275                 break;
276         }
277     }
278
279     /* Add the closing quote */
280     SB_AppendChar (Target, '\"');
281 }
282
283
284
285 static void OldStyleComment (void)
286 /* Remove an old style C comment from line. */
287 {
288     /* Remember the current line number, so we can output better error
289      * messages if the comment is not terminated in the current file.
290      */
291     unsigned StartingLine = GetCurrentLine();
292
293     /* Skip the start of comment chars */
294     NextChar ();
295     NextChar ();
296
297     /* Skip the comment */
298     while (CurC != '*' || NextC != '/') {
299         if (CurC == '\0') {
300             if (NextLine () == 0) {
301                 PPError ("End-of-file reached in comment starting at line %u",
302                          StartingLine);
303                 return;
304             }
305         } else {
306             if (CurC == '/' && NextC == '*') {
307                 PPWarning ("`/*' found inside a comment");
308             }
309             NextChar ();
310         }
311     }
312
313     /* Skip the end of comment chars */
314     NextChar ();
315     NextChar ();
316 }
317
318
319
320 static void NewStyleComment (void)
321 /* Remove a new style C comment from line. */
322 {
323     /* Beware: Because line continuation chars are handled when reading
324      * lines, we may only skip til the end of the source line, which
325      * may not be the same as the end of the input line. The end of the
326      * source line is denoted by a lf (\n) character.
327      */
328     do {
329         NextChar ();
330     } while (CurC != '\n' && CurC != '\0');
331     if (CurC == '\n') {
332         NextChar ();
333     }
334 }
335
336
337
338 static void SkipWhitespace (void)
339 /* Skip white space in the input stream. */
340 {
341     while (IsSpace (CurC)) {
342         NextChar ();
343     }
344 }
345
346
347
348 static void CopyQuotedString (StrBuf* Target)
349 /* Copy a single or double quoted string from the input to Target. */
350 {
351     /* Remember the quote character, copy it to the target buffer and skip it */
352     char Quote = CurC;
353     SB_AppendChar (Target, CurC);
354     NextChar ();
355
356     /* Copy the characters inside the string */
357     while (CurC != '\0' && CurC != Quote) {
358         /* Keep an escaped char */
359         if (CurC == '\\') {
360             SB_AppendChar (Target, CurC);
361             NextChar ();
362         }
363         /* Copy the character */
364         SB_AppendChar (Target, CurC);
365         NextChar ();
366     }
367
368     /* If we had a terminating quote, copy it */
369     if (CurC != '\0') {
370         SB_AppendChar (Target, CurC);
371         NextChar ();
372     }
373 }
374
375
376
377 /*****************************************************************************/
378 /*                                Macro stuff                                */
379 /*****************************************************************************/
380
381
382
383 static int MacName (char* Ident)
384 /* Get a macro symbol name into Ident.  If we have an error, print a
385  * diagnostic message and clear the line.
386  */
387 {
388     if (IsSym (Ident) == 0) {
389         PPError ("Identifier expected");
390         ClearLine ();
391         return 0;
392     } else {
393         return 1;
394     }
395 }
396
397
398
399 static void ReadMacroArgs (MacroExp* E)
400 /* Identify the arguments to a macro call */
401 {
402     unsigned    Parens;         /* Number of open parenthesis */
403     StrBuf      Arg = STATIC_STRBUF_INITIALIZER;
404
405     /* Read the actual macro arguments */
406     Parens = 0;
407     while (1) {
408         if (CurC == '(') {
409
410             /* Nested parenthesis */
411             SB_AppendChar (&Arg, CurC);
412             NextChar ();
413             ++Parens;
414
415         } else if (IsQuote (CurC)) {
416
417             /* Quoted string - just copy */
418             CopyQuotedString (&Arg);
419
420         } else if (CurC == ',' || CurC == ')') {
421
422             if (Parens) {
423                 /* Comma or right paren inside nested parenthesis */
424                 if (CurC == ')') {
425                     --Parens;
426                 }
427                 SB_AppendChar (&Arg, CurC);
428                 NextChar ();
429             } else if (CurC == ',' && ME_ArgIsVariadic (E)) {
430                 /* It's a comma, but we're inside a variadic macro argument, so
431                  * just copy it and proceed.
432                  */
433                 SB_AppendChar (&Arg, CurC);
434                 NextChar ();
435             } else {
436                 /* End of actual argument. Remove whitespace from the end. */
437                 while (IsSpace (SB_LookAtLast (&Arg))) {
438                     SB_Drop (&Arg, 1);
439                 }
440
441                 /* If this is not the single empty argument for a macro with
442                  * an empty argument list, remember it.
443                  */
444                 if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) {
445                     ME_AppendActual (E, &Arg);
446                 }
447
448                 /* Check for end of macro param list */
449                 if (CurC == ')') {
450                     NextChar ();
451                     break;
452                 }
453
454                 /* Start the next param */
455                 NextChar ();
456                 SB_Clear (&Arg);
457             }
458         } else if (IsSpace (CurC)) {
459             /* Squeeze runs of blanks within an arg */
460             if (SB_NotEmpty (&Arg)) {
461                 SB_AppendChar (&Arg, ' ');
462             }
463             SkipWhitespace ();
464         } else if (CurC == '/' && NextC == '*') {
465             if (SB_NotEmpty (&Arg)) {
466                 SB_AppendChar (&Arg, ' ');
467             }
468             OldStyleComment ();
469         } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
470             if (SB_NotEmpty (&Arg)) {
471                 SB_AppendChar (&Arg, ' ');
472             }
473             NewStyleComment ();
474         } else if (CurC == '\0') {
475             /* End of line inside macro argument list - read next line */
476             if (SB_NotEmpty (&Arg)) {
477                 SB_AppendChar (&Arg, ' ');
478             }
479             if (NextLine () == 0) {
480                 ClearLine ();
481                 break;
482             }
483         } else {
484             /* Just copy the character */
485             SB_AppendChar (&Arg, CurC);
486             NextChar ();
487         }
488     }
489
490     /* Deallocate string buf resources */
491     SB_Done (&Arg);
492 }
493
494
495
496 static void MacroArgSubst (MacroExp* E)
497 /* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */
498 {
499     ident       Ident;
500     int         ArgIdx;
501     StrBuf*     OldSource;
502     StrBuf*     Arg;
503     int         HaveSpace;
504
505
506     /* Remember the current input and switch to the macro replacement. */
507     int OldIndex = SB_GetIndex (&E->M->Replacement);
508     SB_Reset (&E->M->Replacement);
509     OldSource = InitLine (&E->M->Replacement);
510
511     /* Argument handling loop */
512     while (CurC != '\0') {
513
514         /* If we have an identifier, check if it's a macro */
515         if (IsSym (Ident)) {
516
517             /* Check if it's a macro argument */
518             if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
519
520                 /* A macro argument. Get the corresponding actual argument. */
521                 Arg = ME_GetActual (E, ArgIdx);
522
523                 /* Copy any following whitespace */
524                 HaveSpace = IsSpace (CurC);
525                 if (HaveSpace) {
526                     SkipWhitespace ();
527                 }
528
529                 /* If a ## operator follows, we have to insert the actual
530                  * argument as is, otherwise it must be macro replaced.
531                  */
532                 if (CurC == '#' && NextC == '#') {
533
534                     /* ### Add placemarker if necessary */
535                     SB_Append (&E->Replacement, Arg);
536
537                 } else {
538
539                     /* Replace the formal argument by a macro replaced copy
540                      * of the actual.
541                      */
542                     SB_Reset (Arg);
543                     MacroReplacement (Arg, &E->Replacement);
544
545                     /* If we skipped whitespace before, re-add it now */
546                     if (HaveSpace) {
547                         SB_AppendChar (&E->Replacement, ' ');
548                     }
549                 }
550
551
552             } else {
553
554                 /* An identifier, keep it */
555                 SB_AppendStr (&E->Replacement, Ident);
556
557             }
558
559         } else if (CurC == '#' && NextC == '#') {
560
561             /* ## operator. */
562             NextChar ();
563             NextChar ();
564             SkipWhitespace ();
565
566             /* Since we need to concatenate the token sequences, remove
567              * any whitespace that was added to target, since it must come
568              * from the input.
569              */
570             while (IsSpace (SB_LookAtLast (&E->Replacement))) {
571                 SB_Drop (&E->Replacement, 1);
572             }
573
574             /* If the next token is an identifier which is a macro argument,
575              * replace it, otherwise do nothing.
576              */
577             if (IsSym (Ident)) {
578
579                 /* Check if it's a macro argument */
580                 if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
581
582                     /* Get the corresponding actual argument and add it. */
583                     SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx));
584
585                 } else {
586
587                     /* Just an ordinary identifier - add as is */
588                     SB_AppendStr (&E->Replacement, Ident);
589
590                 }
591             }
592
593         } else if (CurC == '#' && E->M->ArgCount >= 0) {
594
595             /* A # operator within a macro expansion of a function like
596              * macro. Read the following identifier and check if it's a
597              * macro parameter.
598              */
599             NextChar ();
600             SkipWhitespace ();
601             if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) {
602                 PPError ("`#' is not followed by a macro parameter");
603             } else {
604                 /* Make a valid string from Replacement */
605                 Arg = ME_GetActual (E, ArgIdx);
606                 SB_Reset (Arg);
607                 Stringize (Arg, &E->Replacement);
608             }
609
610         } else if (IsQuote (CurC)) {
611             CopyQuotedString (&E->Replacement);
612         } else {
613             SB_AppendChar (&E->Replacement, CurC);
614             NextChar ();
615         }
616     }
617
618 #if 0
619     /* Remove whitespace from the end of the line */
620     while (IsSpace (SB_LookAtLast (&E->Replacement))) {
621         SB_Drop (&E->Replacement, 1);
622     }
623 #endif
624
625     /* Switch back the input */
626     InitLine (OldSource);
627     SB_SetIndex (&E->M->Replacement, OldIndex);
628 }
629
630
631
632 static void MacroCall (StrBuf* Target, Macro* M)
633 /* Process a function like macro */
634 {
635     MacroExp    E;
636
637     /* Eat the left paren */
638     NextChar ();
639
640     /* Initialize our MacroExp structure */
641     InitMacroExp (&E, M);
642
643     /* Read the actual macro arguments */
644     ReadMacroArgs (&E);
645
646     /* Compare formal and actual argument count */
647     if (CollCount (&E.ActualArgs) != (unsigned) M->ArgCount) {
648
649         StrBuf Arg = STATIC_STRBUF_INITIALIZER;
650
651         /* Argument count mismatch */
652         PPError ("Macro argument count mismatch");
653
654         /* Be sure to make enough empty arguments available */
655         while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) {
656             ME_AppendActual (&E, &Arg);
657         }
658     }
659
660     /* Replace macro arguments handling the # and ## operators */
661     MacroArgSubst (&E);
662
663     /* Do macro replacement on the macro that already has the parameters
664      * substituted.
665      */
666     M->Expanding = 1;
667     MacroReplacement (&E.Replacement, Target);
668     M->Expanding = 0;
669
670     /* Free memory allocated for the macro expansion structure */
671     DoneMacroExp (&E);
672 }
673
674
675
676 static void ExpandMacro (StrBuf* Target, Macro* M)
677 /* Expand a macro into Target */
678 {
679 #if 0
680     static unsigned V = 0;
681     printf ("Expanding %s(%u)\n", M->Name, ++V);
682 #endif
683
684     /* Check if this is a function like macro */
685     if (M->ArgCount >= 0) {
686
687         int Whitespace = IsSpace (CurC);
688         if (Whitespace) {
689             SkipWhitespace ();
690         }
691         if (CurC != '(') {
692             /* Function like macro but no parameter list */
693             SB_AppendStr (Target, M->Name);
694             if (Whitespace) {
695                 SB_AppendChar (Target, ' ');
696             }
697         } else {
698             /* Function like macro */
699             MacroCall (Target, M);
700         }
701
702     } else {
703
704         MacroExp E;
705         InitMacroExp (&E, M);
706
707         /* Handle # and ## operators for object like macros */
708         MacroArgSubst (&E);
709
710         /* Do macro replacement on the macro that already has the parameters
711          * substituted.
712          */
713         M->Expanding = 1;
714         MacroReplacement (&E.Replacement, Target);
715         M->Expanding = 0;
716
717         /* Free memory allocated for the macro expansion structure */
718         DoneMacroExp (&E);
719
720     }
721 #if 0
722     printf ("Done with %s(%u)\n", M->Name, V--);
723 #endif
724 }
725
726
727
728 static void DefineMacro (void)
729 /* Handle a macro definition. */
730 {
731     ident       Ident;
732     Macro*      M;
733     Macro*      Existing;
734     int         C89;
735
736     /* Read the macro name */
737     SkipWhitespace ();
738     if (!MacName (Ident)) {
739         return;
740     }
741
742     /* Remember if we're in C89 mode */
743     C89 = (IS_Get (&Standard) == STD_C89);
744
745     /* Get an existing macro definition with this name */
746     Existing = FindMacro (Ident);
747
748     /* Create a new macro definition */
749     M = NewMacro (Ident);
750
751     /* Check if this is a function like macro */
752     if (CurC == '(') {
753
754         /* Skip the left paren */
755         NextChar ();
756
757         /* Set the marker that this is a function like macro */
758         M->ArgCount = 0;
759
760         /* Read the formal parameter list */
761         while (1) {
762
763             /* Skip white space and check for end of parameter list */
764             SkipWhitespace ();
765             if (CurC == ')') {
766                 break;
767             }
768
769             /* The next token must be either an identifier, or - if not in
770              * C89 mode - the ellipsis.
771              */
772             if (!C89 && CurC == '.') {
773                 /* Ellipsis */
774                 NextChar ();
775                 if (CurC != '.' || NextC != '.') {
776                     PPError ("`...' expected");
777                     ClearLine ();
778                     return;
779                 }
780                 NextChar ();
781                 NextChar ();
782
783                 /* Remember that the macro is variadic and use __VA_ARGS__ as
784                  * the argument name.
785                  */
786                 AddMacroArg (M, "__VA_ARGS__");
787                 M->Variadic = 1;
788
789             } else {
790                 /* Must be macro argument name */
791                 if (MacName (Ident) == 0) {
792                     return;
793                 }
794
795                 /* __VA_ARGS__ is only allowed in C89 mode */
796                 if (!C89 && strcmp (Ident, "__VA_ARGS__") == 0) {
797                     PPWarning ("`__VA_ARGS__' can only appear in the expansion "
798                                "of a C99 variadic macro");
799                 }
800
801                 /* Add the macro argument */
802                 AddMacroArg (M, Ident);
803             }
804
805             /* If we had an ellipsis, or the next char is not a comma, we've
806              * reached the end of the macro argument list.
807              */
808             SkipWhitespace ();
809             if (M->Variadic || CurC != ',') {
810                 break;
811             }
812             NextChar ();
813         }
814
815         /* Check for a right paren and eat it if we find one */
816         if (CurC != ')') {
817             PPError ("`)' expected");
818             ClearLine ();
819             return;
820         }
821         NextChar ();
822     }
823
824     /* Skip whitespace before the macro replacement */
825     SkipWhitespace ();
826
827     /* Insert the macro into the macro table and allocate the ActualArgs array */
828     InsertMacro (M);
829
830     /* Remove whitespace and comments from the line, store the preprocessed
831      * line into the macro replacement buffer.
832      */
833     Pass1 (Line, &M->Replacement);
834
835     /* Remove whitespace from the end of the line */
836     while (IsSpace (SB_LookAtLast (&M->Replacement))) {
837         SB_Drop (&M->Replacement, 1);
838     }
839 #if 0
840     printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement));
841 #endif
842
843     /* If we have an existing macro, check if the redefinition is identical.
844      * Print a diagnostic if not.
845      */
846     if (Existing && MacroCmp (M, Existing) != 0) {
847         PPError ("Macro redefinition is not identical");
848     }
849 }
850
851
852
853 /*****************************************************************************/
854 /*                               Preprocessing                               */
855 /*****************************************************************************/
856
857
858
859 static unsigned Pass1 (StrBuf* Source, StrBuf* Target)
860 /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments
861  * and the "defined" operator.
862  */
863 {
864     unsigned    IdentCount;
865     ident       Ident;
866     int         HaveParen;
867
868     /* Switch to the new input source */
869     StrBuf* OldSource = InitLine (Source);
870
871     /* Loop removing ws and comments */
872     IdentCount = 0;
873     while (CurC != '\0') {
874         if (IsSpace (CurC)) {
875             /* Squeeze runs of blanks */
876             if (!IsSpace (SB_LookAtLast (Target))) {
877                 SB_AppendChar (Target, ' ');
878             }
879             SkipWhitespace ();
880         } else if (IsSym (Ident)) {
881             if (Preprocessing && strcmp (Ident, "defined") == 0) {
882                 /* Handle the "defined" operator */
883                 SkipWhitespace ();
884                 HaveParen = 0;
885                 if (CurC == '(') {
886                     HaveParen = 1;
887                     NextChar ();
888                     SkipWhitespace ();
889                 }
890                 if (IsSym (Ident)) {
891                     SB_AppendChar (Target, IsMacro (Ident)? '1' : '0');
892                     if (HaveParen) {
893                         SkipWhitespace ();
894                         if (CurC != ')') {
895                             PPError ("`)' expected");
896                         } else {
897                             NextChar ();
898                         }
899                     }
900                 } else {
901                     PPError ("Identifier expected");
902                     SB_AppendChar (Target, '0');
903                 }
904             } else {
905                 ++IdentCount;
906                 SB_AppendStr (Target, Ident);
907             }
908         } else if (IsQuote (CurC)) {
909             CopyQuotedString (Target);
910         } else if (CurC == '/' && NextC == '*') {
911             if (!IsSpace (SB_LookAtLast (Target))) {
912                 SB_AppendChar (Target, ' ');
913             }
914             OldStyleComment ();
915         } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
916             if (!IsSpace (SB_LookAtLast (Target))) {
917                 SB_AppendChar (Target, ' ');
918             }
919             NewStyleComment ();
920         } else {
921             SB_AppendChar (Target, CurC);
922             NextChar ();
923         }
924     }
925
926     /* Switch back to the old source */
927     InitLine (OldSource);
928
929     /* Return the number of identifiers found in the line */
930     return IdentCount;
931 }
932
933
934
935 static void MacroReplacement (StrBuf* Source, StrBuf* Target)
936 /* Perform macro replacement. */
937 {
938     ident       Ident;
939     Macro*      M;
940
941     /* Remember the current input and switch to Source */
942     StrBuf* OldSource = InitLine (Source);
943
944     /* Loop substituting macros */
945     while (CurC != '\0') {
946         /* If we have an identifier, check if it's a macro */
947         if (IsSym (Ident)) {
948             /* Check if it's a macro */
949             if ((M = FindMacro (Ident)) != 0 && !M->Expanding) {
950                 /* It's a macro, expand it */
951                 ExpandMacro (Target, M);
952             } else {
953                 /* An identifier, keep it */
954                 SB_AppendStr (Target, Ident);
955             }
956         } else if (IsQuote (CurC)) {
957             CopyQuotedString (Target);
958         } else if (IsSpace (CurC)) {
959             if (!IsSpace (SB_LookAtLast (Target))) {
960                 SB_AppendChar (Target, CurC);
961             }
962             NextChar ();
963         } else {
964             SB_AppendChar (Target, CurC);
965             NextChar ();
966         }
967     }
968
969     /* Switch back the input */
970     InitLine (OldSource);
971 }
972
973
974
975 static void PreprocessLine (void)
976 /* Translate one line. */
977 {
978     /* Trim whitespace and remove comments. The function returns the number of
979      * identifiers found. If there were any, we will have to check for macros.
980      */
981     SB_Clear (MLine);
982     if (Pass1 (Line, MLine) > 0) {
983         MLine = InitLine (MLine);
984         SB_Reset (Line);
985         SB_Clear (MLine);
986         MacroReplacement (Line, MLine);
987     }
988
989     /* Read from the new line */
990     SB_Reset (MLine);
991     MLine = InitLine (MLine);
992 }
993
994
995
996 static int PushIf (int Skip, int Invert, int Cond)
997 /* Push a new if level onto the if stack */
998 {
999     /* Check for an overflow of the if stack */
1000     if (IfIndex >= MAX_IFS-1) {
1001         PPError ("Too many nested #if clauses");
1002         return 1;
1003     }
1004
1005     /* Push the #if condition */
1006     ++IfIndex;
1007     if (Skip) {
1008         IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
1009         return 1;
1010     } else {
1011         IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
1012         return (Invert ^ Cond);
1013     }
1014 }
1015
1016
1017
1018 static void DoError (void)
1019 /* Print an error */
1020 {
1021     SkipWhitespace ();
1022     if (CurC == '\0') {
1023         PPError ("Invalid #error directive");
1024     } else {
1025         PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line));
1026     }
1027
1028     /* Clear the rest of line */
1029     ClearLine ();
1030 }
1031
1032
1033
1034 static int DoIf (int Skip)
1035 /* Process #if directive */
1036 {
1037     ExprDesc Expr;
1038
1039     /* We're about to abuse the compiler expression parser to evaluate the
1040      * #if expression. Save the current tokens to come back here later.
1041      * NOTE: Yes, this is a hack, but it saves a complete separate expression
1042      * evaluation for the preprocessor.
1043      */
1044     Token SavedCurTok  = CurTok;
1045     Token SavedNextTok = NextTok;
1046
1047     /* Make sure the line infos for the tokens won't get removed */
1048     if (SavedCurTok.LI) {
1049         UseLineInfo (SavedCurTok.LI);
1050     }
1051     if (SavedNextTok.LI) {
1052         UseLineInfo (SavedNextTok.LI);
1053     }
1054
1055     /* Switch into special preprocessing mode */
1056     Preprocessing = 1;
1057
1058     /* Expand macros in this line */
1059     PreprocessLine ();
1060
1061     /* Add two semicolons as sentinels to the line, so the following
1062      * expression evaluation will eat these two tokens but nothing from
1063      * the following line.
1064      */
1065     SB_AppendStr (Line, ";;");
1066     SB_Terminate (Line);
1067
1068     /* Load CurTok and NextTok with tokens from the new input */
1069     NextToken ();
1070     NextToken ();
1071
1072     /* Call the expression parser */
1073     ConstExpr (hie1, &Expr);
1074
1075     /* End preprocessing mode */
1076     Preprocessing = 0;
1077
1078     /* Reset the old tokens */
1079     CurTok  = SavedCurTok;
1080     NextTok = SavedNextTok;
1081
1082     /* Set the #if condition according to the expression result */
1083     return PushIf (Skip, 1, Expr.IVal != 0);
1084 }
1085
1086
1087
1088 static int DoIfDef (int skip, int flag)
1089 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
1090 {
1091     ident Ident;
1092
1093     SkipWhitespace ();
1094     if (MacName (Ident) == 0) {
1095         return 0;
1096     } else {
1097         return PushIf (skip, flag, IsMacro(Ident));
1098     }
1099 }
1100
1101
1102
1103 static void DoInclude (void)
1104 /* Open an include file. */
1105 {
1106     char        RTerm;
1107     unsigned    DirSpec;
1108     StrBuf      Filename = STATIC_STRBUF_INITIALIZER;
1109
1110
1111     /* Preprocess the remainder of the line */
1112     PreprocessLine ();
1113
1114     /* Skip blanks */
1115     SkipWhitespace ();
1116
1117     /* Get the next char and check for a valid file name terminator. Setup
1118      * the include directory spec (SYS/USR) by looking at the terminator.
1119      */
1120     switch (CurC) {
1121
1122         case '\"':
1123             RTerm   = '\"';
1124             DirSpec = INC_USER;
1125             break;
1126
1127         case '<':
1128             RTerm   = '>';
1129             DirSpec = INC_SYS;
1130             break;
1131
1132         default:
1133             PPError ("`\"' or `<' expected");
1134             goto Done;
1135     }
1136     NextChar ();
1137
1138     /* Get a copy of the filename */
1139     while (CurC != '\0' && CurC != RTerm) {
1140         SB_AppendChar (&Filename, CurC);
1141         NextChar ();
1142     }
1143     SB_Terminate (&Filename);
1144
1145     /* Check if we got a terminator */
1146     if (CurC == RTerm) {
1147         /* Open the include file */
1148         OpenIncludeFile (SB_GetConstBuf (&Filename), DirSpec);
1149     } else if (CurC == '\0') {
1150         /* No terminator found */
1151         PPError ("#include expects \"FILENAME\" or <FILENAME>");
1152     }
1153
1154 Done:
1155     /* Free the allocated filename data */
1156     SB_Done (&Filename);
1157
1158     /* Clear the remaining line so the next input will come from the new
1159      * file (if open)
1160      */
1161     ClearLine ();
1162 }
1163
1164
1165
1166 static void DoPragma (void)
1167 /* Handle a #pragma line by converting the #pragma preprocessor directive into
1168  * the _Pragma() compiler operator.
1169  */
1170 {
1171     /* Skip blanks following the #pragma directive */
1172     SkipWhitespace ();
1173
1174     /* Copy the remainder of the line into MLine removing comments and ws */
1175     SB_Clear (MLine);
1176     Pass1 (Line, MLine);
1177
1178     /* Convert the directive into the operator */
1179     SB_CopyStr (Line, "_Pragma (");
1180     SB_Reset (MLine);
1181     Stringize (MLine, Line);
1182     SB_AppendChar (Line, ')');
1183
1184     /* Initialize reading from line */
1185     SB_Reset (Line);
1186     InitLine (Line);
1187 }
1188
1189
1190
1191 static void DoUndef (void)
1192 /* Process the #undef directive */
1193 {
1194     ident Ident;
1195
1196     SkipWhitespace ();
1197     if (MacName (Ident)) {
1198         UndefineMacro (Ident);
1199     }
1200 }
1201
1202
1203
1204 static void DoWarning (void)
1205 /* Print a warning */
1206 {
1207     SkipWhitespace ();
1208     if (CurC == '\0') {
1209         PPError ("Invalid #warning directive");
1210     } else {
1211         PPWarning ("#warning: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line));
1212     }
1213
1214     /* Clear the rest of line */
1215     ClearLine ();
1216 }
1217
1218
1219
1220 void Preprocess (void)
1221 /* Preprocess a line */
1222 {
1223     int         Skip;
1224     ident       Directive;
1225
1226     /* Create the output buffer if we don't already have one */
1227     if (MLine == 0) {
1228         MLine = NewStrBuf ();
1229     }
1230
1231     /* Skip white space at the beginning of the line */
1232     SkipWhitespace ();
1233
1234     /* Check for stuff to skip */
1235     Skip = 0;
1236     while (CurC == '\0' || CurC == '#' || Skip) {
1237
1238         /* Check for preprocessor lines lines */
1239         if (CurC == '#') {
1240             NextChar ();
1241             SkipWhitespace ();
1242             if (CurC == '\0') {
1243                 /* Ignore the empty preprocessor directive */
1244                 continue;
1245             }
1246             if (!IsSym (Directive)) {
1247                 PPError ("Preprocessor directive expected");
1248                 ClearLine ();
1249             } else {
1250                 switch (FindPPToken (Directive)) {
1251
1252                     case PP_DEFINE:
1253                         if (!Skip) {
1254                             DefineMacro ();
1255                         }
1256                         break;
1257
1258                     case PP_ELIF:
1259                         if (IfIndex >= 0) {
1260                             if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
1261
1262                                 /* Handle as #else/#if combination */
1263                                 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
1264                                     Skip = !Skip;
1265                                 }
1266                                 IfStack[IfIndex] |= IFCOND_ELSE;
1267                                 Skip = DoIf (Skip);
1268
1269                                 /* #elif doesn't need a terminator */
1270                                 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
1271                             } else {
1272                                 PPError ("Duplicate #else/#elif");
1273                             }
1274                         } else {
1275                             PPError ("Unexpected #elif");
1276                         }
1277                         break;
1278
1279                     case PP_ELSE:
1280                         if (IfIndex >= 0) {
1281                             if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
1282                                 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
1283                                     Skip = !Skip;
1284                                 }
1285                                 IfStack[IfIndex] |= IFCOND_ELSE;
1286                             } else {
1287                                 PPError ("Duplicate #else");
1288                             }
1289                         } else {
1290                             PPError ("Unexpected `#else'");
1291                         }
1292                         break;
1293
1294                     case PP_ENDIF:
1295                         if (IfIndex >= 0) {
1296                             /* Remove any clauses on top of stack that do not
1297                              * need a terminating #endif.
1298                              */
1299                             while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
1300                                 --IfIndex;
1301                             }
1302
1303                             /* Stack may not be empty here or something is wrong */
1304                             CHECK (IfIndex >= 0);
1305
1306                             /* Remove the clause that needs a terminator */
1307                             Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
1308                         } else {
1309                             PPError ("Unexpected `#endif'");
1310                         }
1311                         break;
1312
1313                     case PP_ERROR:
1314                         if (!Skip) {
1315                             DoError ();
1316                         }
1317                         break;
1318
1319                     case PP_IF:
1320                         Skip = DoIf (Skip);
1321                         break;
1322
1323                     case PP_IFDEF:
1324                         Skip = DoIfDef (Skip, 1);
1325                         break;
1326
1327                     case PP_IFNDEF:
1328                         Skip = DoIfDef (Skip, 0);
1329                         break;
1330
1331                     case PP_INCLUDE:
1332                         if (!Skip) {
1333                             DoInclude ();
1334                         }
1335                         break;
1336
1337                     case PP_LINE:
1338                         /* Should do something in C99 at least, but we ignore it */
1339                         if (!Skip) {
1340                             ClearLine ();
1341                         }
1342                         break;
1343
1344                     case PP_PRAGMA:
1345                         if (!Skip) {
1346                             DoPragma ();
1347                             goto Done;
1348                         }
1349                         break;
1350
1351                     case PP_UNDEF:
1352                         if (!Skip) {
1353                             DoUndef ();
1354                         }
1355                         break;
1356
1357                     case PP_WARNING:
1358                         /* #warning is a non standard extension */
1359                         if (IS_Get (&Standard) > STD_C99) {
1360                             if (!Skip) {
1361                                 DoWarning ();
1362                             }
1363                         } else {
1364                             if (!Skip) {
1365                                 PPError ("Preprocessor directive expected");
1366                             }
1367                             ClearLine ();
1368                         }
1369                         break;
1370
1371                     default: 
1372                         if (!Skip) {
1373                             PPError ("Preprocessor directive expected");
1374                         }
1375                         ClearLine ();
1376                 }
1377             }
1378
1379         }
1380         if (NextLine () == 0) {
1381             if (IfIndex >= 0) {
1382                 PPError ("`#endif' expected");
1383             }
1384             return;
1385         }
1386         SkipWhitespace ();
1387     }
1388
1389     PreprocessLine ();
1390
1391 Done:
1392     if (Verbosity > 1 && SB_NotEmpty (Line)) {
1393         printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (),
1394                 (int) SB_GetLen (Line), SB_GetConstBuf (Line));
1395     }
1396 }
1397