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