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