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