]> git.sur5r.net Git - cc65/blob - src/cc65/preproc.c
4877dad3b6a6b3c5a9a6e0f6cdbc2edfe61f8860
[cc65] / src / cc65 / preproc.c
1
2 /* C pre-processor functions */
3
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <errno.h>
8 #include <ctype.h>
9
10 #include "../common/xmalloc.h"
11
12 #include "codegen.h"
13 #include "error.h"
14 #include "expr.h"
15 #include "global.h"
16 #include "ident.h"
17 #include "incpath.h"
18 #include "input.h"
19 #include "macrotab.h"
20 #include "scanner.h"
21 #include "util.h"
22 #include "preproc.h"
23
24
25
26 /*****************************************************************************/
27 /*                                 Forwards                                  */
28 /*****************************************************************************/
29
30
31
32 static int Pass1 (const char* From, char* To);
33 /* Preprocessor pass 1. Remove whitespace and comments. */
34
35
36
37 /*****************************************************************************/
38 /*                                   data                                    */
39 /*****************************************************************************/
40
41
42
43 /* Set when the pp calls expr() recursively */
44 unsigned char Preprocessing = 0;
45
46 /* Management data for #if */
47 #define N_IFDEF         16
48 static int i_ifdef = -1;
49 static char s_ifdef[N_IFDEF];
50
51 /* Buffer for macro expansion */
52 static char mlinebuf [LINESIZE];
53 static char* mline = mlinebuf;
54 static char* mptr;
55
56 /* Flag: Expand macros in this line */
57 static int ExpandMacros = 1;
58
59
60
61 /*****************************************************************************/
62 /*                                   code                                    */
63 /*****************************************************************************/
64
65
66
67 static int keepch (char c)
68 /* Put character c into translation buffer. */
69 {
70     return (*mptr++ = c);
71 }
72
73
74
75 static void keepstr (const char* S)
76 /* Put string str into translation buffer. */
77 {
78     while (*S) {
79         keepch (*S++);
80     }
81 }
82
83
84
85 static void Comment (void)
86 /* Remove a C comment from line. */
87 {
88     /* Remember the current line number, so we can output better error
89      * messages if the comment is not terminated in the current file.
90      */
91     unsigned StartingLine = GetCurrentLine();
92
93     /* Skip the start of comment chars */
94     NextChar ();
95     NextChar ();
96
97     /* Skip the comment */
98     while (CurC != '*' || NextC != '/') {
99         if (CurC == '\0') {
100             if (NextLine () == 0) {
101                 PPError ("End-of-file reached in comment starting at line %u",
102                          StartingLine);
103                 return;
104             }
105         } else {
106             if (CurC == '/' && NextC == '*') {
107                 PPWarning ("`/*' found inside a comment");
108             }
109             NextChar ();
110         }
111     }
112
113     /* Skip the end of comment chars */
114     NextChar ();
115     NextChar ();
116 }
117
118
119
120 static void SkipBlank (void)
121 /* Skip blanks and tabs in the input stream. */
122 {
123     while (IsBlank (CurC)) {
124         NextChar ();
125     }
126 }
127
128
129
130 static char* CopyQuotedString (char* Target)
131 /* Copy a single or double quoted string from the input to Target. Return the
132  * new target pointer. Target will not be terminated after the copy.
133  */
134 {
135     /* Remember the quote character, copy it to the target buffer and skip it */
136     char Quote = CurC;
137     *Target++  = CurC;
138     NextChar ();
139
140     /* Copy the characters inside the string */
141     while (CurC != '\0' && CurC != Quote) {
142         /* Keep an escaped char */
143         if (CurC == '\\') {
144             *Target++ = CurC;
145             NextChar ();
146         }
147         /* Copy the character */
148         *Target++ = CurC;
149         NextChar ();
150     }
151
152     /* If we had a terminating quote, copy it */
153     if (CurC != '\0') {
154         *Target++ = CurC;
155         NextChar ();
156     }
157
158     /* Return the new target pointer */
159     return Target;
160 }
161
162
163
164 /*****************************************************************************/
165 /*                                Macro stuff                                */
166 /*****************************************************************************/
167
168
169
170 static int MacName (char* Ident)
171 /* Get macro symbol name.  If error, print message and clear line. */
172 {
173     if (IsSym (Ident) == 0) {
174         PPError ("Identifier expected");
175         ClearLine ();
176         return 0;
177     } else {
178         return 1;
179     }
180 }
181
182
183
184 static void ExpandMacroArgs (Macro* M)
185 /* Preprocessor pass 2.  Perform macro substitution. */
186 {
187     ident       Ident;
188     const char* Replacement;
189     const char* SavePtr;
190
191     /* Save the current line pointer and setup the new ones */
192     SavePtr = lptr;
193     InitLine (M->Replacement);
194
195     /* Copy the macro replacement checking for parameters to replace */
196     while (CurC != '\0') {
197         /* If the next token is an identifier, check for a macro arg */
198         if (IsIdent (CurC)) {
199             SymName (Ident);
200             Replacement = FindMacroArg (M, Ident);
201             if (Replacement) {
202                 /* Macro arg, keep the replacement */
203                 keepstr (Replacement);
204             } else {
205                 /* No macro argument, keep the original identifier */
206                 keepstr (Ident);
207             }
208         } else if (CurC == '#' && IsIdent (NextC)) {
209             NextChar ();
210             SymName (Ident);
211             Replacement = FindMacroArg (M, Ident);
212             if (Replacement) {
213                 keepch ('\"');
214                 keepstr (Replacement);
215                 keepch ('\"');
216             } else {
217                 keepch ('#');
218                 keepstr (Ident);
219             }
220         } else if (IsQuoteChar (CurC)) {
221             mptr = CopyQuotedString (mptr);
222         } else {
223             *mptr++ = CurC;
224             NextChar ();
225         }
226     }
227
228     /* Reset the line pointer */
229     InitLine (SavePtr);
230 }
231
232
233
234 static int MacroCall (Macro* M)
235 /* Process a function like macro */
236 {
237     unsigned    ArgCount;       /* Macro argument count */
238     unsigned    ParCount;       /* Number of open parenthesis */
239     char        Buf[LINESIZE];  /* Argument buffer */
240     const char* ArgStart;
241     char*       B;
242
243     /* Expect an argument list */
244     SkipBlank ();
245     if (CurC != '(') {
246         PPError ("Illegal macro call");
247         return 0;
248     }
249
250     /* Eat the left paren */
251     NextChar ();
252
253     /* Read the actual macro arguments and store pointers to these arguments
254      * into the array of actual arguments in the macro definition.
255      */
256     ArgCount = 0;
257     ParCount = 0;
258     ArgStart = Buf;
259     B        = Buf;
260     while (1) {
261         if (CurC == '(') {
262             /* Nested parenthesis */
263             *B++ = CurC;
264             NextChar ();
265             ++ParCount;
266         } else if (IsQuoteChar (CurC)) {
267             B = CopyQuotedString (B);
268         } else if (CurC == ',' || CurC == ')') {
269             if (ParCount == 0) {
270                 /* End of actual argument */
271                 *B++ = '\0';
272                 while (IsBlank(*ArgStart)) {
273                     ++ArgStart;
274                 }
275                 if (ArgCount < M->ArgCount) {
276                     M->ActualArgs[ArgCount++] = ArgStart;
277                 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
278                     /* Be sure not to count the single empty argument for a
279                      * macro that does not have arguments.
280                      */
281                     ++ArgCount;
282                 }
283
284                 /* Check for end of macro param list */
285                 if (CurC == ')') {
286                     NextChar ();
287                     break;
288                 }
289
290                 /* Start the next param */
291                 ArgStart = B;
292                 NextChar ();
293             } else {
294                 /* Comma or right paren inside nested parenthesis */
295                 if (CurC == ')') {
296                     --ParCount;
297                 }
298                 *B++ = CurC;
299                 NextChar ();
300             }
301         } else if (IsBlank (CurC)) {
302             /* Squeeze runs of blanks */
303             *B++ = ' ';
304             SkipBlank ();
305         } else if (CurC == '\0') {
306             /* End of line inside macro argument list - read next line */
307             if (NextLine () == 0) {
308                 return 0;
309             }
310         } else {
311             /* Just copy the character */
312             *B++ = CurC;
313             NextChar ();
314         }
315     }
316
317     /* Compare formal argument count with actual */
318     if (M->ArgCount != ArgCount) {
319         PPError ("Macro argument count mismatch");
320         /* Be sure to make enough empty arguments available */
321         while (ArgCount < M->ArgCount) {
322             M->ActualArgs [ArgCount++] = "";
323         }
324     }
325
326     /* Preprocess the line, replacing macro parameters */
327     ExpandMacroArgs (M);
328
329     /* Done */
330     return 1;
331 }
332
333
334
335 static void ExpandMacro (Macro* M)
336 /* Expand a macro */
337 {
338     /* Check if this is a function like macro */
339     if (M->ArgCount >= 0) {
340         /* Function like macro */
341         if (MacroCall (M) == 0) {
342             ClearLine ();
343         }
344     } else {
345         /* Just copy the replacement text */
346         keepstr (M->Replacement);
347     }
348 }
349
350
351
352 static void addmac (void)
353 /* Add a macro to the macro table. */
354 {
355     char*       saveptr;
356     ident       Ident;
357     char        Buf[LINESIZE];
358     Macro*      M;
359     Macro*      Existing;
360
361     /* Read the macro name */
362     SkipBlank ();
363     if (!MacName (Ident)) {
364         return;
365     }
366
367     /* Get an existing macro definition with this name */
368     Existing = FindMacro (Ident);
369
370     /* Create a new macro definition */
371     M = NewMacro (Ident);
372
373     /* Check if this is a function like macro */
374     if (CurC == '(') {
375
376         /* Skip the left paren */
377         NextChar ();
378
379         /* Set the marker that this is a function like macro */
380         M->ArgCount = 0;
381
382         /* Read the formal parameter list */
383         while (1) {
384             SkipBlank ();
385             if (CurC == ')')
386                 break;
387             if (MacName (Ident) == 0) {
388                 return;
389             }
390             AddMacroArg (M, Ident);
391             SkipBlank ();
392             if (CurC != ',')
393                 break;
394             NextChar ();
395         }
396
397         /* Check for a right paren and eat it if we find one */
398         if (CurC != ')') {
399             PPError ("`)' expected");
400             ClearLine ();
401             return;
402         }
403         NextChar ();
404     }
405
406     /* Insert the macro into the macro table and allocate the ActualArgs array */
407     InsertMacro (M);
408
409     /* Remove whitespace and comments from the line, store the preprocessed
410      * line into Buf.
411      */
412     SkipBlank ();
413     saveptr = mptr;
414     Pass1 (lptr, Buf);
415     mptr = saveptr;
416
417     /* Create a copy of the replacement */
418     M->Replacement = xstrdup (Buf);
419
420     /* If we have an existing macro, check if the redefinition is identical.
421      * Print a diagnostic if not.
422      */
423     if (Existing) {
424         if (MacroCmp (M, Existing) != 0) {
425             PPError ("Macro redefinition is not identical");
426         }
427     }
428 }
429
430
431
432 /*****************************************************************************/
433
434 /*****************************************************************************/
435
436
437
438 static int Pass1 (const char* From, char* To)
439 /* Preprocessor pass 1. Remove whitespace and comments. */
440 {
441     int         done;
442     ident       Ident;
443     int         HaveParen;
444
445     /* Initialize reading from "From" */
446     InitLine (From);
447
448     /* Target is "To" */
449     mptr = To;
450
451     /* Loop removing ws and comments */
452     done = 1;
453     while (CurC != '\0') {
454         if (IsBlank (CurC)) {
455             keepch (' ');
456             SkipBlank ();
457         } else if (IsIdent (CurC)) {
458             SymName (Ident);
459             if (Preprocessing && strcmp(Ident, "defined") == 0) {
460                 /* Handle the "defined" operator */
461                 SkipBlank();
462                 HaveParen = 0;
463                 if (CurC == '(') {
464                     HaveParen = 1;
465                     NextChar ();
466                     SkipBlank();
467                 }
468                 if (!IsIdent (CurC)) {
469                     PPError ("Identifier expected");
470                     *mptr++ = '0';
471                 } else {
472                     SymName (Ident);
473                     *mptr++ = IsMacro (Ident)? '1' : '0';
474                     if (HaveParen) {
475                         SkipBlank();
476                         if (CurC != ')') {
477                             PPError ("`)' expected");
478                         } else {
479                             NextChar ();
480                         }
481                     }
482                 }
483             } else {
484                 if (MaybeMacro (Ident[0])) {
485                     done = 0;
486                 }
487                 keepstr (Ident);
488             }
489         } else if (IsQuoteChar (CurC)) {
490             mptr = CopyQuotedString (mptr);
491         } else if (CurC == '/' && NextC == '*') {
492             keepch (' ');
493             Comment ();
494         } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
495             keepch (' ');
496             /* Beware: Because line continuation chars are handled when reading
497              * lines, we may only skip til the end of the source line, which
498              * may not be the same as the end of the input line. The end of the
499              * source line is denoted by a lf (\n) character.
500              */
501             do {
502                 NextChar ();
503             } while (CurC != '\n' && CurC != '\0');
504             if (CurC == '\n') {
505                 NextChar ();
506             }
507         } else {
508             *mptr++ = CurC;
509             NextChar ();
510         }
511     }
512     keepch ('\0');
513     return done;
514 }
515
516
517
518 static int Pass2 (const char* From, char* To)
519 /* Preprocessor pass 2.  Perform macro substitution. */
520 {
521     int         no_chg;
522     ident       Ident;
523     Macro*      M;
524
525     /* Initialize reading from "From" */
526     InitLine (From);
527
528     /* Target is "To" */
529     mptr = To;
530
531     /* Loop substituting macros */
532     no_chg = 1;
533     while (CurC != '\0') {
534         /* If we have an identifier, check if it's a macro */
535         if (IsIdent (CurC)) {
536             SymName (Ident);
537             M = FindMacro (Ident);
538             if (M) {
539                 ExpandMacro (M);
540                 no_chg = 0;
541             } else {
542                 keepstr (Ident);
543             }
544         } else if (IsQuoteChar(CurC)) {
545             mptr = CopyQuotedString (mptr);
546         } else {
547             *mptr++ = CurC;
548             NextChar ();
549         }
550     }
551     return no_chg;
552 }
553
554
555
556 static void xlateline (void)
557 /* Translate one line. */
558 {
559     int cnt;
560     int Done;
561
562     Done = Pass1 (line, mline);
563     if (ExpandMacros == 0) {
564         Done = 1;
565         ExpandMacros = 1;       /* Reset to default */
566     }
567     cnt = 5;
568     do {
569         /* Swap mline and line */
570         char* p = line;
571         line = mline;
572         mline = p;
573         if (Done)
574             break;
575         Done = Pass2 (line, mline);
576         keepch ('\0');
577     } while (--cnt);
578
579     /* Reinitialize line parsing */
580     InitLine (line);
581 }
582
583
584
585 static void doundef (void)
586 /* Process #undef directive */
587 {
588     ident Ident;
589
590     SkipBlank ();
591     if (MacName (Ident)) {
592         UndefineMacro (Ident);
593     }
594 }
595
596
597
598 static int setmflag (int skip, int flag, int cond)
599 /* setmflag( skip, flag, cond ) */
600 {
601     if (skip) {
602         s_ifdef[++i_ifdef] = 3;
603         return (1);
604     } else {
605         s_ifdef[++i_ifdef] = 6;
606         return (flag ^ cond);
607     }
608 }
609
610
611
612 static int doiff (int skip)
613 /* Process #if directive */
614 {
615     struct expent lval;
616     char* S;
617
618     /* We're about to abuse the compiler expression parser to evaluate the
619      * #if expression. Save the current tokens to come back here later.
620      */
621     Token sv1 = CurTok;
622     Token sv2 = NextTok;
623
624     /* Remove the #if from the line and add two semicolons as sentinels */
625     SkipBlank ();
626     S = line;
627     while (CurC != '\0') {
628         *S++ = CurC;
629         NextChar ();
630     }
631     *S++ = ';';
632     *S++ = ';';
633     *S   = '\0';
634
635     /* Start over parsing from line */
636     InitLine (line);
637
638     /* Switch into special preprocessing mode */
639     Preprocessing = 1;
640
641     /* Expand macros in this line */
642     xlateline ();
643
644     /* Prime the token pump (remove old tokens from the stream) */
645     NextToken ();
646     NextToken ();
647
648     /* Call the expression parser */
649     constexpr (&lval);
650
651     /* End preprocessing mode */
652     Preprocessing = 0;
653
654     /* Reset the old tokens */
655     CurTok  = sv1;
656     NextTok = sv2;
657
658     /* Set the #if condition according to the expression result */
659     return (setmflag (skip, 1, lval.e_const != 0));
660 }
661
662
663
664 static int doifdef (int skip, int flag)
665 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
666 {
667     ident Ident;
668
669     SkipBlank ();
670     if (MacName (Ident) == 0) {
671         return 0;
672     } else {
673         return setmflag (skip, flag, IsMacro(Ident));
674     }
675 }
676
677
678
679 static void doinclude (void)
680 /* Open an include file. */
681 {
682     char        RTerm;
683     unsigned    DirSpec;
684
685
686     /* Skip blanks */
687     SkipBlank ();
688
689     /* Get the next char and check for a valid file name terminator. Setup
690      * the include directory spec (SYS/USR) by looking at the terminator.
691      */
692     switch (CurC) {
693
694         case '\"':
695             RTerm   = '\"';
696             DirSpec = INC_USER;
697             break;
698
699         case '<':
700             RTerm   = '>';
701             DirSpec = INC_SYS;
702             break;
703
704         default:
705             PPError ("`\"' or `<' expected");
706             goto Done;
707     }
708     NextChar ();
709
710     /* Copy the filename into mline. Since mline has the same size as the
711      * input line, we don't need to check for an overflow here.
712      */
713     mptr = mline;
714     while (CurC != '\0' && CurC != RTerm) {
715         *mptr++ = CurC;
716         NextChar ();
717     }
718     *mptr = '\0';
719
720     /* Check if we got a terminator */
721     if (CurC != RTerm) {
722         /* No terminator found */
723         PPError ("Missing terminator or file name too long");
724         goto Done;
725     }
726
727     /* Open the include file */
728     OpenIncludeFile (mline, DirSpec);
729
730 Done:
731     /* Clear the remaining line so the next input will come from the new
732      * file (if open)
733      */
734     ClearLine ();
735 }
736
737
738
739 static void doerror (void)
740 /* Print an error */
741 {
742     SkipBlank ();
743     if (CurC == '\0') {
744         PPError ("Invalid #error directive");
745     } else {
746         PPError ("#error: %s", lptr);
747     }
748
749     /* clear rest of line */
750     ClearLine ();
751 }
752
753
754
755 /* C preprocessor. */
756
757 /* stuff used to bum the keyword dispatching stuff */
758 enum {
759     PP_DEFINE,
760     PP_ELSE,
761     PP_ENDIF,
762     PP_ERROR,
763     PP_IF,
764     PP_IFDEF,
765     PP_IFNDEF,
766     PP_INCLUDE,
767     PP_LINE,
768     PP_PRAGMA,
769     PP_UNDEF,
770     PP_ILLEGAL
771 };
772
773 static const struct tok_elt pre_toks[] = {
774     {   "define",       PP_DEFINE       },
775     {   "else",         PP_ELSE         },
776     {   "endif",        PP_ENDIF        },
777     {   "error",        PP_ERROR        },
778     {   "if",           PP_IF           },
779     {   "ifdef",        PP_IFDEF        },
780     {   "ifndef",       PP_IFNDEF       },
781     {   "include",      PP_INCLUDE      },
782     {   "line",         PP_LINE         },
783     {   "pragma",       PP_PRAGMA       },
784     {   "undef",        PP_UNDEF        },
785     {   0,              PP_ILLEGAL      }
786 };
787
788
789
790 static int searchtok (const char *sym, const struct tok_elt *toks)
791 /* Search a token in a table */
792 {
793     while (toks->toknam && strcmp (toks->toknam, sym))
794         ++toks;
795     return (toks->toknbr);
796 }
797
798
799
800 void Preprocess (void)
801 /* Preprocess a line */
802 {
803     int         Skip;
804     ident       Directive;
805
806     /* Skip white space at the beginning of the line */
807     SkipBlank ();
808
809     /* Check for stuff to skip */
810     Skip = 0;
811     while (CurC == '\0' || CurC == '#' || Skip) {
812
813         /* Check for preprocessor lines lines */
814         if (CurC == '#') {
815             NextChar ();
816             SkipBlank ();
817             if (CurC == '\0') {
818                 /* Ignore the empty preprocessor directive */
819                 continue;
820             }
821             if (!IsSym (Directive)) {
822                 PPError ("Preprocessor directive expected");
823                 ClearLine ();
824             } else {
825                 switch (searchtok (Directive, pre_toks)) {
826
827                     case PP_DEFINE:
828                         if (!Skip) {
829                             addmac ();
830                         }
831                         break;
832
833                     case PP_ELSE:
834                         if (s_ifdef[i_ifdef] & 2) {
835                             if (s_ifdef[i_ifdef] & 4) {
836                                 Skip = !Skip;
837                             }
838                             s_ifdef[i_ifdef] ^= 2;
839                         } else {
840                             PPError ("Unexpected `#else'");
841                         }
842                         break;
843
844                     case PP_ENDIF:
845                         if (i_ifdef >= 0) {
846                             Skip = s_ifdef[i_ifdef--] & 1;
847                         } else {
848                             PPError ("Unexpected `#endif'");
849                         }
850                         break;
851
852                     case PP_ERROR:
853                         if (!Skip) {
854                             doerror ();
855                         }
856                         break;
857
858                     case PP_IF:
859                         Skip = doiff (Skip);
860                         break;
861
862                     case PP_IFDEF:
863                         Skip = doifdef (Skip, 1);
864                         break;
865
866                     case PP_IFNDEF:
867                         Skip = doifdef (Skip, 0);
868                         break;
869
870                     case PP_INCLUDE:
871                         if (!Skip) {
872                             doinclude ();
873                         }
874                         break;
875
876                     case PP_LINE:
877                         /* Not allowed in strict ANSI mode */
878                         if (ANSI) {
879                             PPError ("Preprocessor directive expected");
880                             ClearLine ();
881                         }
882                         break;
883
884                     case PP_PRAGMA:
885                         if (!Skip) {
886                             /* Don't expand macros in this line */
887                             ExpandMacros = 0;
888                             /* #pragma is handled on the scanner level */
889                             goto Done;
890                         }
891                         break;
892
893                     case PP_UNDEF:
894                         if (!Skip) {
895                             doundef ();
896                         }
897                         break;
898
899                     default:
900                         PPError ("Preprocessor directive expected");
901                         ClearLine ();
902                 }
903             }
904
905         }
906         if (NextLine () == 0) {
907             if (i_ifdef >= 0) {
908                 PPError ("`#endif' expected");
909             }
910             return;
911         }
912         SkipBlank ();
913     }
914
915 Done:
916     xlateline ();
917     if (Verbose > 1) {
918         printf ("line: %s\n", line);
919     }
920 }
921