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