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