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 */
71unsigned 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
79static unsigned char IfStack[MAX_IFS];
80static int IfIndex = -1;
81
82/* Buffer for macro expansion */
83static StrBuf* MLine;
84
85/* Structure used when expanding macros */
86typedef struct MacroExp MacroExp;
87struct 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
101static 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
106static 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 */
118typedef 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 */
138static 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
162static 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
170static 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
188static 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
199static 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
214static 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
231static 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
239static 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
254static 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
285static 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
320static 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
338static 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
367static 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
402static 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
418static 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
511static 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
644static 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
688static 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
737static 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
868static 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
943static 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
983static 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
1004static 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
1026static 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
1042static 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
1096static 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
1111static 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
1162Done:
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
1174static 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
1199static 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
1212static 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
1228void 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
1399Done:
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}
1405