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