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