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