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