]> git.sur5r.net Git - cc65/blob - src/cc65/preproc.c
Renamed a C header.
[cc65] / src / cc65 / preproc.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  preproc.c                                */
4 /*                                                                           */
5 /*                              cc65 preprocessor                            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2010, 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 until 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 int SkipWhitespace (int SkipLines)
339 /* Skip white space in the input stream. Do also skip newlines if SkipLines
340 ** is true. Return zero if nothing was skipped, otherwise return a
341 ** value != zero.
342 */
343 {
344     int Skipped = 0;
345     while (1) {
346         if (IsSpace (CurC)) {
347             NextChar ();
348             Skipped = 1;
349         } else if (CurC == '\0' && SkipLines) {
350             /* End of line, read next */
351             if (NextLine () != 0) {
352                 Skipped = 1;
353             } else {
354                 /* End of input */
355                 break;
356             }
357         } else {
358             /* No more white space */
359             break;
360         }
361     }
362     return Skipped;
363 }
364
365
366
367 static void CopyQuotedString (StrBuf* Target)
368 /* Copy a single or double quoted string from the input to Target. */
369 {
370     /* Remember the quote character, copy it to the target buffer and skip it */
371     char Quote = CurC;
372     SB_AppendChar (Target, CurC);
373     NextChar ();
374
375     /* Copy the characters inside the string */
376     while (CurC != '\0' && CurC != Quote) {
377         /* Keep an escaped char */
378         if (CurC == '\\') {
379             SB_AppendChar (Target, CurC);
380             NextChar ();
381         }
382         /* Copy the character */
383         SB_AppendChar (Target, CurC);
384         NextChar ();
385     }
386
387     /* If we had a terminating quote, copy it */
388     if (CurC != '\0') {
389         SB_AppendChar (Target, CurC);
390         NextChar ();
391     }
392 }
393
394
395
396 /*****************************************************************************/
397 /*                                Macro stuff                                */
398 /*****************************************************************************/
399
400
401
402 static int MacName (char* Ident)
403 /* Get a macro symbol name into Ident.  If we have an error, print a
404 ** diagnostic message and clear the line.
405 */
406 {
407     if (IsSym (Ident) == 0) {
408         PPError ("Identifier expected");
409         ClearLine ();
410         return 0;
411     } else {
412         return 1;
413     }
414 }
415
416
417
418 static void ReadMacroArgs (MacroExp* E)
419 /* Identify the arguments to a macro call */
420 {
421     unsigned    Parens;         /* Number of open parenthesis */
422     StrBuf      Arg = STATIC_STRBUF_INITIALIZER;
423
424     /* Read the actual macro arguments */
425     Parens = 0;
426     while (1) {
427         if (CurC == '(') {
428
429             /* Nested parenthesis */
430             SB_AppendChar (&Arg, CurC);
431             NextChar ();
432             ++Parens;
433
434         } else if (IsQuote (CurC)) {
435
436             /* Quoted string - just copy */
437             CopyQuotedString (&Arg);
438
439         } else if (CurC == ',' || CurC == ')') {
440
441             if (Parens) {
442                 /* Comma or right paren inside nested parenthesis */
443                 if (CurC == ')') {
444                     --Parens;
445                 }
446                 SB_AppendChar (&Arg, CurC);
447                 NextChar ();
448             } else if (CurC == ',' && ME_ArgIsVariadic (E)) {
449                 /* It's a comma, but we're inside a variadic macro argument, so
450                 ** just copy it and proceed.
451                 */
452                 SB_AppendChar (&Arg, CurC);
453                 NextChar ();
454             } else {
455                 /* End of actual argument. Remove whitespace from the end. */
456                 while (IsSpace (SB_LookAtLast (&Arg))) {
457                     SB_Drop (&Arg, 1);
458                 }
459
460                 /* If this is not the single empty argument for a macro with
461                 ** an empty argument list, remember it.
462                 */
463                 if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) {
464                     ME_AppendActual (E, &Arg);
465                 }
466
467                 /* Check for end of macro param list */
468                 if (CurC == ')') {
469                     NextChar ();
470                     break;
471                 }
472
473                 /* Start the next param */
474                 NextChar ();
475                 SB_Clear (&Arg);
476             }
477         } else if (SkipWhitespace (1)) {
478             /* Squeeze runs of blanks within an arg */
479             if (SB_NotEmpty (&Arg)) {
480                 SB_AppendChar (&Arg, ' ');
481             }
482         } else if (CurC == '/' && NextC == '*') {
483             if (SB_NotEmpty (&Arg)) {
484                 SB_AppendChar (&Arg, ' ');
485             }
486             OldStyleComment ();
487         } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
488             if (SB_NotEmpty (&Arg)) {
489                 SB_AppendChar (&Arg, ' ');
490             }
491             NewStyleComment ();
492         } else if (CurC == '\0') {
493             /* End of input inside macro argument list */
494             PPError ("Unterminated argument list invoking macro '%s'", E->M->Name);
495
496             ClearLine ();
497             break;
498         } else {
499             /* Just copy the character */
500             SB_AppendChar (&Arg, CurC);
501             NextChar ();
502         }
503     }
504
505     /* Deallocate string buf resources */
506     SB_Done (&Arg);
507 }
508
509
510
511 static void MacroArgSubst (MacroExp* E)
512 /* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */
513 {
514     ident       Ident;
515     int         ArgIdx;
516     StrBuf*     OldSource;
517     StrBuf*     Arg;
518     int         HaveSpace;
519
520
521     /* Remember the current input and switch to the macro replacement. */
522     int OldIndex = SB_GetIndex (&E->M->Replacement);
523     SB_Reset (&E->M->Replacement);
524     OldSource = InitLine (&E->M->Replacement);
525
526     /* Argument handling loop */
527     while (CurC != '\0') {
528
529         /* If we have an identifier, check if it's a macro */
530         if (IsSym (Ident)) {
531
532             /* Check if it's a macro argument */
533             if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
534
535                 /* A macro argument. Get the corresponding actual argument. */
536                 Arg = ME_GetActual (E, ArgIdx);
537
538                 /* Copy any following whitespace */
539                 HaveSpace = SkipWhitespace (0);
540
541                 /* If a ## operator follows, we have to insert the actual
542                 ** argument as is, otherwise it must be macro replaced.
543                 */
544                 if (CurC == '#' && NextC == '#') {
545
546                     /* ### Add placemarker if necessary */
547                     SB_Append (&E->Replacement, Arg);
548
549                 } else {
550
551                     /* Replace the formal argument by a macro replaced copy
552                     ** of the actual.
553                     */
554                     SB_Reset (Arg);
555                     MacroReplacement (Arg, &E->Replacement);
556
557                     /* If we skipped whitespace before, re-add it now */
558                     if (HaveSpace) {
559                         SB_AppendChar (&E->Replacement, ' ');
560                     }
561                 }
562
563
564             } else {
565
566                 /* An identifier, keep it */
567                 SB_AppendStr (&E->Replacement, Ident);
568
569             }
570
571         } else if (CurC == '#' && NextC == '#') {
572
573             /* ## operator. */
574             NextChar ();
575             NextChar ();
576             SkipWhitespace (0);
577
578             /* Since we need to concatenate the token sequences, remove
579             ** any whitespace that was added to target, since it must come
580             ** from the input.
581             */
582             while (IsSpace (SB_LookAtLast (&E->Replacement))) {
583                 SB_Drop (&E->Replacement, 1);
584             }
585
586             /* If the next token is an identifier which is a macro argument,
587             ** replace it, otherwise do nothing.
588             */
589             if (IsSym (Ident)) {
590
591                 /* Check if it's a macro argument */
592                 if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
593
594                     /* Get the corresponding actual argument and add it. */
595                     SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx));
596
597                 } else {
598
599                     /* Just an ordinary identifier - add as is */
600                     SB_AppendStr (&E->Replacement, Ident);
601
602                 }
603             }
604
605         } else if (CurC == '#' && E->M->ArgCount >= 0) {
606
607             /* A # operator within a macro expansion of a function like
608             ** macro. Read the following identifier and check if it's a
609             ** macro parameter.
610             */
611             NextChar ();
612             SkipWhitespace (0);
613             if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) {
614                 PPError ("'#' is not followed by a macro parameter");
615             } else {
616                 /* Make a valid string from Replacement */
617                 Arg = ME_GetActual (E, ArgIdx);
618                 SB_Reset (Arg);
619                 Stringize (Arg, &E->Replacement);
620             }
621
622         } else if (IsQuote (CurC)) {
623             CopyQuotedString (&E->Replacement);
624         } else {
625             SB_AppendChar (&E->Replacement, CurC);
626             NextChar ();
627         }
628     }
629
630 #if 0
631     /* Remove whitespace from the end of the line */
632     while (IsSpace (SB_LookAtLast (&E->Replacement))) {
633         SB_Drop (&E->Replacement, 1);
634     }
635 #endif
636
637     /* Switch back the input */
638     InitLine (OldSource);
639     SB_SetIndex (&E->M->Replacement, OldIndex);
640 }
641
642
643
644 static void MacroCall (StrBuf* Target, Macro* M)
645 /* Process a function like macro */
646 {
647     MacroExp    E;
648
649     /* Eat the left paren */
650     NextChar ();
651
652     /* Initialize our MacroExp structure */
653     InitMacroExp (&E, M);
654
655     /* Read the actual macro arguments */
656     ReadMacroArgs (&E);
657
658     /* Compare formal and actual argument count */
659     if (CollCount (&E.ActualArgs) != (unsigned) M->ArgCount) {
660
661         StrBuf Arg = STATIC_STRBUF_INITIALIZER;
662
663         /* Argument count mismatch */
664         PPError ("Macro argument count mismatch");
665
666         /* Be sure to make enough empty arguments available */
667         while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) {
668             ME_AppendActual (&E, &Arg);
669         }
670     }
671
672     /* Replace macro arguments handling the # and ## operators */
673     MacroArgSubst (&E);
674
675     /* Do macro replacement on the macro that already has the parameters
676     ** substituted.
677     */
678     M->Expanding = 1;
679     MacroReplacement (&E.Replacement, Target);
680     M->Expanding = 0;
681
682     /* Free memory allocated for the macro expansion structure */
683     DoneMacroExp (&E);
684 }
685
686
687
688 static void ExpandMacro (StrBuf* Target, Macro* M)
689 /* Expand a macro into Target */
690 {
691 #if 0
692     static unsigned V = 0;
693     printf ("Expanding %s(%u)\n", M->Name, ++V);
694 #endif
695
696     /* Check if this is a function like macro */
697     if (M->ArgCount >= 0) {
698
699         int Whitespace = SkipWhitespace (1);
700         if (CurC != '(') {
701             /* Function like macro but no parameter list */
702             SB_AppendStr (Target, M->Name);
703             if (Whitespace) {
704                 SB_AppendChar (Target, ' ');
705             }
706         } else {
707             /* Function like macro */
708             MacroCall (Target, M);
709         }
710
711     } else {
712
713         MacroExp E;
714         InitMacroExp (&E, M);
715
716         /* Handle # and ## operators for object like macros */
717         MacroArgSubst (&E);
718
719         /* Do macro replacement on the macro that already has the parameters
720         ** substituted.
721         */
722         M->Expanding = 1;
723         MacroReplacement (&E.Replacement, Target);
724         M->Expanding = 0;
725
726         /* Free memory allocated for the macro expansion structure */
727         DoneMacroExp (&E);
728
729     }
730 #if 0
731     printf ("Done with %s(%u)\n", M->Name, V--);
732 #endif
733 }
734
735
736
737 static void DefineMacro (void)
738 /* Handle a macro definition. */
739 {
740     ident       Ident;
741     Macro*      M;
742     Macro*      Existing;
743     int         C89;
744
745     /* Read the macro name */
746     SkipWhitespace (0);
747     if (!MacName (Ident)) {
748         return;
749     }
750
751     /* Remember if we're in C89 mode */
752     C89 = (IS_Get (&Standard) == STD_C89);
753
754     /* Get an existing macro definition with this name */
755     Existing = FindMacro (Ident);
756
757     /* Create a new macro definition */
758     M = NewMacro (Ident);
759
760     /* Check if this is a function like macro */
761     if (CurC == '(') {
762
763         /* Skip the left paren */
764         NextChar ();
765
766         /* Set the marker that this is a function like macro */
767         M->ArgCount = 0;
768
769         /* Read the formal parameter list */
770         while (1) {
771
772             /* Skip white space and check for end of parameter list */
773             SkipWhitespace (0);
774             if (CurC == ')') {
775                 break;
776             }
777
778             /* The next token must be either an identifier, or - if not in
779             ** C89 mode - the ellipsis.
780             */
781             if (!C89 && CurC == '.') {
782                 /* Ellipsis */
783                 NextChar ();
784                 if (CurC != '.' || NextC != '.') {
785                     PPError ("'...' expected");
786                     ClearLine ();
787                     return;
788                 }
789                 NextChar ();
790                 NextChar ();
791
792                 /* Remember that the macro is variadic and use __VA_ARGS__ as
793                 ** the argument name.
794                 */
795                 AddMacroArg (M, "__VA_ARGS__");
796                 M->Variadic = 1;
797
798             } else {
799                 /* Must be macro argument name */
800                 if (MacName (Ident) == 0) {
801                     return;
802                 }
803
804                 /* __VA_ARGS__ is only allowed in C89 mode */
805                 if (!C89 && strcmp (Ident, "__VA_ARGS__") == 0) {
806                     PPWarning ("'__VA_ARGS__' can only appear in the expansion "
807                                "of a C99 variadic macro");
808                 }
809
810                 /* Add the macro argument */
811                 AddMacroArg (M, Ident);
812             }
813
814             /* If we had an ellipsis, or the next char is not a comma, we've
815             ** reached the end of the macro argument list.
816             */
817             SkipWhitespace (0);
818             if (M->Variadic || CurC != ',') {
819                 break;
820             }
821             NextChar ();
822         }
823
824         /* Check for a right paren and eat it if we find one */
825         if (CurC != ')') {
826             PPError ("')' expected");
827             ClearLine ();
828             return;
829         }
830         NextChar ();
831     }
832
833     /* Skip whitespace before the macro replacement */
834     SkipWhitespace (0);
835
836     /* Insert the macro into the macro table and allocate the ActualArgs array */
837     InsertMacro (M);
838
839     /* Remove whitespace and comments from the line, store the preprocessed
840     ** line into the macro replacement buffer.
841     */
842     Pass1 (Line, &M->Replacement);
843
844     /* Remove whitespace from the end of the line */
845     while (IsSpace (SB_LookAtLast (&M->Replacement))) {
846         SB_Drop (&M->Replacement, 1);
847     }
848 #if 0
849     printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement));
850 #endif
851
852     /* If we have an existing macro, check if the redefinition is identical.
853     ** Print a diagnostic if not.
854     */
855     if (Existing && MacroCmp (M, Existing) != 0) {
856         PPError ("Macro redefinition is not identical");
857     }
858 }
859
860
861
862 /*****************************************************************************/
863 /*                               Preprocessing                               */
864 /*****************************************************************************/
865
866
867
868 static unsigned Pass1 (StrBuf* Source, StrBuf* Target)
869 /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments
870 ** and the "defined" operator.
871 */
872 {
873     unsigned    IdentCount;
874     ident       Ident;
875     int         HaveParen;
876
877     /* Switch to the new input source */
878     StrBuf* OldSource = InitLine (Source);
879
880     /* Loop removing ws and comments */
881     IdentCount = 0;
882     while (CurC != '\0') {
883         if (SkipWhitespace (0)) {
884             /* Squeeze runs of blanks */
885             if (!IsSpace (SB_LookAtLast (Target))) {
886                 SB_AppendChar (Target, ' ');
887             }
888         } else if (IsSym (Ident)) {
889             if (Preprocessing && strcmp (Ident, "defined") == 0) {
890                 /* Handle the "defined" operator */
891                 SkipWhitespace (0);
892                 HaveParen = 0;
893                 if (CurC == '(') {
894                     HaveParen = 1;
895                     NextChar ();
896                     SkipWhitespace (0);
897                 }
898                 if (IsSym (Ident)) {
899                     SB_AppendChar (Target, IsMacro (Ident)? '1' : '0');
900                     if (HaveParen) {
901                         SkipWhitespace (0);
902                         if (CurC != ')') {
903                             PPError ("')' expected");
904                         } else {
905                             NextChar ();
906                         }
907                     }
908                 } else {
909                     PPError ("Identifier expected");
910                     SB_AppendChar (Target, '0');
911                 }
912             } else {
913                 ++IdentCount;
914                 SB_AppendStr (Target, Ident);
915             }
916         } else if (IsQuote (CurC)) {
917             CopyQuotedString (Target);
918         } else if (CurC == '/' && NextC == '*') {
919             if (!IsSpace (SB_LookAtLast (Target))) {
920                 SB_AppendChar (Target, ' ');
921             }
922             OldStyleComment ();
923         } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
924             if (!IsSpace (SB_LookAtLast (Target))) {
925                 SB_AppendChar (Target, ' ');
926             }
927             NewStyleComment ();
928         } else {
929             SB_AppendChar (Target, CurC);
930             NextChar ();
931         }
932     }
933
934     /* Switch back to the old source */
935     InitLine (OldSource);
936
937     /* Return the number of identifiers found in the line */
938     return IdentCount;
939 }
940
941
942
943 static void MacroReplacement (StrBuf* Source, StrBuf* Target)
944 /* Perform macro replacement. */
945 {
946     ident       Ident;
947     Macro*      M;
948
949     /* Remember the current input and switch to Source */
950     StrBuf* OldSource = InitLine (Source);
951
952     /* Loop substituting macros */
953     while (CurC != '\0') {
954         /* If we have an identifier, check if it's a macro */
955         if (IsSym (Ident)) {
956             /* Check if it's a macro */
957             if ((M = FindMacro (Ident)) != 0 && !M->Expanding) {
958                 /* It's a macro, expand it */
959                 ExpandMacro (Target, M);
960             } else {
961                 /* An identifier, keep it */
962                 SB_AppendStr (Target, Ident);
963             }
964         } else if (IsQuote (CurC)) {
965             CopyQuotedString (Target);
966         } else if (IsSpace (CurC)) {
967             if (!IsSpace (SB_LookAtLast (Target))) {
968                 SB_AppendChar (Target, CurC);
969             }
970             NextChar ();
971         } else {
972             SB_AppendChar (Target, CurC);
973             NextChar ();
974         }
975     }
976
977     /* Switch back the input */
978     InitLine (OldSource);
979 }
980
981
982
983 static void PreprocessLine (void)
984 /* Translate one line. */
985 {
986     /* Trim whitespace and remove comments. The function returns the number of
987     ** identifiers found. If there were any, we will have to check for macros.
988     */
989     SB_Clear (MLine);
990     if (Pass1 (Line, MLine) > 0) {
991         MLine = InitLine (MLine);
992         SB_Reset (Line);
993         SB_Clear (MLine);
994         MacroReplacement (Line, MLine);
995     }
996
997     /* Read from the new line */
998     SB_Reset (MLine);
999     MLine = InitLine (MLine);
1000 }
1001
1002
1003
1004 static int PushIf (int Skip, int Invert, int Cond)
1005 /* Push a new if level onto the if stack */
1006 {
1007     /* Check for an overflow of the if stack */
1008     if (IfIndex >= MAX_IFS-1) {
1009         PPError ("Too many nested #if clauses");
1010         return 1;
1011     }
1012
1013     /* Push the #if condition */
1014     ++IfIndex;
1015     if (Skip) {
1016         IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
1017         return 1;
1018     } else {
1019         IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
1020         return (Invert ^ Cond);
1021     }
1022 }
1023
1024
1025
1026 static void DoError (void)
1027 /* Print an error */
1028 {
1029     SkipWhitespace (0);
1030     if (CurC == '\0') {
1031         PPError ("Invalid #error directive");
1032     } else {
1033         PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line));
1034     }
1035
1036     /* Clear the rest of line */
1037     ClearLine ();
1038 }
1039
1040
1041
1042 static int DoIf (int Skip)
1043 /* Process #if directive */
1044 {
1045     ExprDesc Expr;
1046
1047     /* We're about to abuse the compiler expression parser to evaluate the
1048     ** #if expression. Save the current tokens to come back here later.
1049     ** NOTE: Yes, this is a hack, but it saves a complete separate expression
1050     ** evaluation for the preprocessor.
1051     */
1052     Token SavedCurTok  = CurTok;
1053     Token SavedNextTok = NextTok;
1054
1055     /* Make sure the line infos for the tokens won't get removed */
1056     if (SavedCurTok.LI) {
1057         UseLineInfo (SavedCurTok.LI);
1058     }
1059     if (SavedNextTok.LI) {
1060         UseLineInfo (SavedNextTok.LI);
1061     }
1062
1063     /* Switch into special preprocessing mode */
1064     Preprocessing = 1;
1065
1066     /* Expand macros in this line */
1067     PreprocessLine ();
1068
1069     /* Add two semicolons as sentinels to the line, so the following
1070     ** expression evaluation will eat these two tokens but nothing from
1071     ** the following line.
1072     */
1073     SB_AppendStr (Line, ";;");
1074     SB_Terminate (Line);
1075
1076     /* Load CurTok and NextTok with tokens from the new input */
1077     NextToken ();
1078     NextToken ();
1079
1080     /* Call the expression parser */
1081     ConstExpr (hie1, &Expr);
1082
1083     /* End preprocessing mode */
1084     Preprocessing = 0;
1085
1086     /* Reset the old tokens */
1087     CurTok  = SavedCurTok;
1088     NextTok = SavedNextTok;
1089
1090     /* Set the #if condition according to the expression result */
1091     return PushIf (Skip, 1, Expr.IVal != 0);
1092 }
1093
1094
1095
1096 static int DoIfDef (int skip, int flag)
1097 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
1098 {
1099     ident Ident;
1100
1101     SkipWhitespace (0);
1102     if (MacName (Ident) == 0) {
1103         return 0;
1104     } else {
1105         return PushIf (skip, flag, IsMacro(Ident));
1106     }
1107 }
1108
1109
1110
1111 static void DoInclude (void)
1112 /* Open an include file. */
1113 {
1114     char        RTerm;
1115     InputType   IT;
1116     StrBuf      Filename = STATIC_STRBUF_INITIALIZER;
1117
1118
1119     /* Preprocess the remainder of the line */
1120     PreprocessLine ();
1121
1122     /* Skip blanks */
1123     SkipWhitespace (0);
1124
1125     /* Get the next char and check for a valid file name terminator. Setup
1126     ** the include directory spec (SYS/USR) by looking at the terminator.
1127     */
1128     switch (CurC) {
1129
1130         case '\"':
1131             RTerm   = '\"';
1132             IT = IT_USRINC;
1133             break;
1134
1135         case '<':
1136             RTerm   = '>';
1137             IT = IT_SYSINC;
1138             break;
1139
1140         default:
1141             PPError ("'\"' or '<' expected");
1142             goto Done;
1143     }
1144     NextChar ();
1145
1146     /* Get a copy of the filename */
1147     while (CurC != '\0' && CurC != RTerm) {
1148         SB_AppendChar (&Filename, CurC);
1149         NextChar ();
1150     }
1151     SB_Terminate (&Filename);
1152
1153     /* Check if we got a terminator */
1154     if (CurC == RTerm) {
1155         /* Open the include file */
1156         OpenIncludeFile (SB_GetConstBuf (&Filename), IT);
1157     } else if (CurC == '\0') {
1158         /* No terminator found */
1159         PPError ("#include expects \"FILENAME\" or <FILENAME>");
1160     }
1161
1162 Done:
1163     /* Free the allocated filename data */
1164     SB_Done (&Filename);
1165
1166     /* Clear the remaining line so the next input will come from the new
1167     ** file (if open)
1168     */
1169     ClearLine ();
1170 }
1171
1172
1173
1174 static void DoPragma (void)
1175 /* Handle a #pragma line by converting the #pragma preprocessor directive into
1176 ** the _Pragma() compiler operator.
1177 */
1178 {
1179     /* Skip blanks following the #pragma directive */
1180     SkipWhitespace (0);
1181
1182     /* Copy the remainder of the line into MLine removing comments and ws */
1183     SB_Clear (MLine);
1184     Pass1 (Line, MLine);
1185
1186     /* Convert the directive into the operator */
1187     SB_CopyStr (Line, "_Pragma (");
1188     SB_Reset (MLine);
1189     Stringize (MLine, Line);
1190     SB_AppendChar (Line, ')');
1191
1192     /* Initialize reading from line */
1193     SB_Reset (Line);
1194     InitLine (Line);
1195 }
1196
1197
1198
1199 static void DoUndef (void)
1200 /* Process the #undef directive */
1201 {
1202     ident Ident;
1203
1204     SkipWhitespace (0);
1205     if (MacName (Ident)) {
1206         UndefineMacro (Ident);
1207     }
1208 }
1209
1210
1211
1212 static void DoWarning (void)
1213 /* Print a warning */
1214 {
1215     SkipWhitespace (0);
1216     if (CurC == '\0') {
1217         PPError ("Invalid #warning directive");
1218     } else {
1219         PPWarning ("#warning: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line));
1220     }
1221
1222     /* Clear the rest of line */
1223     ClearLine ();
1224 }
1225
1226
1227
1228 void Preprocess (void)
1229 /* Preprocess a line */
1230 {
1231     int         Skip;
1232     ident       Directive;
1233
1234     /* Create the output buffer if we don't already have one */
1235     if (MLine == 0) {
1236         MLine = NewStrBuf ();
1237     }
1238
1239     /* Skip white space at the beginning of the line */
1240     SkipWhitespace (0);
1241
1242     /* Check for stuff to skip */
1243     Skip = 0;
1244     while (CurC == '\0' || CurC == '#' || Skip) {
1245
1246         /* Check for preprocessor lines lines */
1247         if (CurC == '#') {
1248             NextChar ();
1249             SkipWhitespace (0);
1250             if (CurC == '\0') {
1251                 /* Ignore the empty preprocessor directive */
1252                 continue;
1253             }
1254             if (!IsSym (Directive)) {
1255                 PPError ("Preprocessor directive expected");
1256                 ClearLine ();
1257             } else {
1258                 switch (FindPPToken (Directive)) {
1259
1260                     case PP_DEFINE:
1261                         if (!Skip) {
1262                             DefineMacro ();
1263                         }
1264                         break;
1265
1266                     case PP_ELIF:
1267                         if (IfIndex >= 0) {
1268                             if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
1269
1270                                 /* Handle as #else/#if combination */
1271                                 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
1272                                     Skip = !Skip;
1273                                 }
1274                                 IfStack[IfIndex] |= IFCOND_ELSE;
1275                                 Skip = DoIf (Skip);
1276
1277                                 /* #elif doesn't need a terminator */
1278                                 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
1279                             } else {
1280                                 PPError ("Duplicate #else/#elif");
1281                             }
1282                         } else {
1283                             PPError ("Unexpected #elif");
1284                         }
1285                         break;
1286
1287                     case PP_ELSE:
1288                         if (IfIndex >= 0) {
1289                             if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
1290                                 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
1291                                     Skip = !Skip;
1292                                 }
1293                                 IfStack[IfIndex] |= IFCOND_ELSE;
1294                             } else {
1295                                 PPError ("Duplicate #else");
1296                             }
1297                         } else {
1298                             PPError ("Unexpected '#else'");
1299                         }
1300                         break;
1301
1302                     case PP_ENDIF:
1303                         if (IfIndex >= 0) {
1304                             /* Remove any clauses on top of stack that do not
1305                             ** need a terminating #endif.
1306                             */
1307                             while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
1308                                 --IfIndex;
1309                             }
1310
1311                             /* Stack may not be empty here or something is wrong */
1312                             CHECK (IfIndex >= 0);
1313
1314                             /* Remove the clause that needs a terminator */
1315                             Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
1316                         } else {
1317                             PPError ("Unexpected '#endif'");
1318                         }
1319                         break;
1320
1321                     case PP_ERROR:
1322                         if (!Skip) {
1323                             DoError ();
1324                         }
1325                         break;
1326
1327                     case PP_IF:
1328                         Skip = DoIf (Skip);
1329                         break;
1330
1331                     case PP_IFDEF:
1332                         Skip = DoIfDef (Skip, 1);
1333                         break;
1334
1335                     case PP_IFNDEF:
1336                         Skip = DoIfDef (Skip, 0);
1337                         break;
1338
1339                     case PP_INCLUDE:
1340                         if (!Skip) {
1341                             DoInclude ();
1342                         }
1343                         break;
1344
1345                     case PP_LINE:
1346                         /* Should do something in C99 at least, but we ignore it */
1347                         if (!Skip) {
1348                             ClearLine ();
1349                         }
1350                         break;
1351
1352                     case PP_PRAGMA:
1353                         if (!Skip) {
1354                             DoPragma ();
1355                             goto Done;
1356                         }
1357                         break;
1358
1359                     case PP_UNDEF:
1360                         if (!Skip) {
1361                             DoUndef ();
1362                         }
1363                         break;
1364
1365                     case PP_WARNING:
1366                         /* #warning is a non standard extension */
1367                         if (IS_Get (&Standard) > STD_C99) {
1368                             if (!Skip) {
1369                                 DoWarning ();
1370                             }
1371                         } else {
1372                             if (!Skip) {
1373                                 PPError ("Preprocessor directive expected");
1374                             }
1375                             ClearLine ();
1376                         }
1377                         break;
1378
1379                     default:
1380                         if (!Skip) {
1381                             PPError ("Preprocessor directive expected");
1382                         }
1383                         ClearLine ();
1384                 }
1385             }
1386
1387         }
1388         if (NextLine () == 0) {
1389             if (IfIndex >= 0) {
1390                 PPError ("'#endif' expected");
1391             }
1392             return;
1393         }
1394         SkipWhitespace (0);
1395     }
1396
1397     PreprocessLine ();
1398
1399 Done:
1400     if (Verbosity > 1 && SB_NotEmpty (Line)) {
1401         printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (),
1402                 (int) SB_GetLen (Line), SB_GetConstBuf (Line));
1403     }
1404 }