]> git.sur5r.net Git - cc65/blob - src/cc65/preproc.c
Added the io module
[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
359     /* Read the macro name */
360     SkipBlank ();
361     if (!MacName (Ident)) {
362         return;
363     }
364
365     /* Create a new macro definition */
366     M = NewMacro (Ident);
367
368     /* Check if this is a function like macro */
369     if (CurC == '(') {
370
371         /* Skip the left paren */
372         NextChar ();
373
374         /* Set the marker that this is a function like macro */
375         M->ArgCount = 0;
376
377         /* Read the formal parameter list */
378         while (1) {
379             SkipBlank ();
380             if (CurC == ')')
381                 break;
382             if (MacName (Ident) == 0) {
383                 return;
384             }
385             AddMacroArg (M, Ident);
386             SkipBlank ();
387             if (CurC != ',')
388                 break;
389             NextChar ();
390         }
391
392         /* Check for a right paren and eat it if we find one */
393         if (CurC != ')') {
394             PPError (ERR_RPAREN_EXPECTED);
395             ClearLine ();
396             return;
397         }
398         NextChar ();
399     }
400
401     /* Insert the macro into the macro table and allocate the ActualArgs array */
402     InsertMacro (M);
403
404     /* Remove whitespace and comments from the line, store the preprocessed
405      * line into Buf.
406      */
407     SkipBlank ();
408     saveptr = mptr;
409     Pass1 (lptr, Buf);
410     mptr = saveptr;
411
412     /* Create a copy of the replacement */
413     M->Replacement = xstrdup (Buf);
414 }
415
416
417
418 /*****************************************************************************/
419
420 /*****************************************************************************/
421
422
423
424 static int Pass1 (const char* From, char* To)
425 /* Preprocessor pass 1. Remove whitespace and comments. */
426 {
427     int         done;
428     ident       Ident;
429     int         HaveParen;
430
431     /* Initialize reading from "From" */
432     InitLine (From);
433
434     /* Target is "To" */
435     mptr = To;
436
437     /* Loop removing ws and comments */
438     done = 1;
439     while (CurC != '\0') {
440         if (IsBlank (CurC)) {
441             keepch (' ');
442             SkipBlank ();
443         } else if (IsIdent (CurC)) {
444             SymName (Ident);
445             if (Preprocessing && strcmp(Ident, "defined") == 0) {
446                 /* Handle the "defined" operator */
447                 SkipBlank();
448                 HaveParen = 0;
449                 if (CurC == '(') {
450                     HaveParen = 1;
451                     NextChar ();
452                     SkipBlank();
453                 }
454                 if (!IsIdent (CurC)) {
455                     PPError (ERR_IDENT_EXPECTED);
456                     *mptr++ = '0';
457                 } else {
458                     SymName (Ident);
459                     *mptr++ = IsMacro (Ident)? '1' : '0';
460                     if (HaveParen) {
461                         SkipBlank();
462                         if (CurC != ')') {
463                             PPError (ERR_RPAREN_EXPECTED);
464                         } else {
465                             NextChar ();
466                         }
467                     }
468                 }
469             } else {
470                 if (MaybeMacro (Ident[0])) {
471                     done = 0;
472                 }
473                 keepstr (Ident);
474             }
475         } else if (IsQuoteChar (CurC)) {
476             mptr = CopyQuotedString (mptr);
477         } else if (CurC == '/' && NextC == '*') {
478             keepch (' ');
479             Comment ();
480         } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
481             keepch (' ');
482             /* Beware: Because line continuation chars are handled when reading
483              * lines, we may only skip til the end of the source line, which
484              * may not be the same as the end of the input line. The end of the
485              * source line is denoted by a lf (\n) character.
486              */
487             do {
488                 NextChar ();
489             } while (CurC != '\n' && CurC != '\0');
490             if (CurC == '\n') {
491                 NextChar ();
492             }
493         } else {
494             *mptr++ = CurC;
495             NextChar ();
496         }
497     }
498     keepch ('\0');
499     return done;
500 }
501
502
503
504 static int Pass2 (const char* From, char* To)
505 /* Preprocessor pass 2.  Perform macro substitution. */
506 {
507     int         no_chg;
508     ident       Ident;
509     Macro*      M;
510
511     /* Initialize reading from "From" */
512     InitLine (From);
513
514     /* Target is "To" */
515     mptr = To;
516
517     /* Loop substituting macros */
518     no_chg = 1;
519     while (CurC != '\0') {
520         /* If we have an identifier, check if it's a macro */
521         if (IsIdent (CurC)) {
522             SymName (Ident);
523             M = FindMacro (Ident);
524             if (M) {
525                 ExpandMacro (M);
526                 no_chg = 0;
527             } else {
528                 keepstr (Ident);
529             }
530         } else if (IsQuoteChar(CurC)) {
531             mptr = CopyQuotedString (mptr);
532         } else {
533             *mptr++ = CurC;
534             NextChar ();
535         }
536     }
537     return no_chg;
538 }
539
540
541
542 static void xlateline (void)
543 /* Translate one line. */
544 {
545     int cnt;
546     int Done;
547
548     Done = Pass1 (line, mline);
549     if (ExpandMacros == 0) {
550         Done = 1;
551         ExpandMacros = 1;       /* Reset to default */
552     }
553     cnt = 5;
554     do {
555         /* Swap mline and line */
556         char* p = line;
557         line = mline;
558         mline = p;
559         if (Done)
560             break;
561         Done = Pass2 (line, mline);
562         keepch ('\0');
563     } while (--cnt);
564
565     /* Reinitialize line parsing */
566     InitLine (line);
567 }
568
569
570
571 static void doundef (void)
572 /* Process #undef directive */
573 {
574     ident Ident;
575
576     SkipBlank ();
577     if (MacName (Ident)) {
578         UndefineMacro (Ident);
579     }
580 }
581
582
583
584 static int setmflag (int skip, int flag, int cond)
585 /* setmflag( skip, flag, cond ) */
586 {
587     if (skip) {
588         s_ifdef[++i_ifdef] = 3;
589         return (1);
590     } else {
591         s_ifdef[++i_ifdef] = 6;
592         return (flag ^ cond);
593     }
594 }
595
596
597
598 static int doiff (int skip)
599 /* Process #if directive */
600 {
601     struct expent lval;
602     char* S;
603
604     /* We're about to abuse the compiler expression parser to evaluate the
605      * #if expression. Save the current tokens to come back here later.
606      */
607     Token sv1 = CurTok;
608     Token sv2 = NextTok;
609
610     /* Remove the #if from the line and add two semicolons as sentinels */
611     SkipBlank ();
612     S = line;
613     while (CurC != '\0') {
614         *S++ = CurC;
615         NextChar ();
616     }
617     *S++ = ';';
618     *S++ = ';';
619     *S   = '\0';
620
621     /* Start over parsing from line */
622     InitLine (line);
623
624     /* Switch into special preprocessing mode */
625     Preprocessing = 1;
626
627     /* Expand macros in this line */
628     xlateline ();
629
630     /* Prime the token pump (remove old tokens from the stream) */
631     NextToken ();
632     NextToken ();
633
634     /* Call the expression parser */
635     constexpr (&lval);
636
637     /* End preprocessing mode */
638     Preprocessing = 0;
639
640     /* Reset the old tokens */
641     CurTok  = sv1;
642     NextTok = sv2;
643
644     /* Set the #if condition according to the expression result */
645     return (setmflag (skip, 1, lval.e_const != 0));
646 }
647
648
649
650 static int doifdef (int skip, int flag)
651 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
652 {
653     ident Ident;
654
655     SkipBlank ();
656     if (MacName (Ident) == 0) {
657         return 0;
658     } else {
659         return setmflag (skip, flag, IsMacro(Ident));
660     }
661 }
662
663
664
665 static void doinclude (void)
666 /* Open an include file. */
667 {
668     char        RTerm;
669     unsigned    DirSpec;
670
671
672     /* Skip blanks */
673     SkipBlank ();
674
675     /* Get the next char and check for a valid file name terminator. Setup
676      * the include directory spec (SYS/USR) by looking at the terminator.
677      */
678     switch (CurC) {
679
680         case '\"':
681             RTerm   = '\"';
682             DirSpec = INC_USER;
683             break;
684
685         case '<':
686             RTerm   = '>';
687             DirSpec = INC_SYS;
688             break;
689
690         default:
691             PPError (ERR_INCLUDE_LTERM_EXPECTED);
692             goto Done;
693     }
694     NextChar ();
695
696     /* Copy the filename into mline. Since mline has the same size as the
697      * input line, we don't need to check for an overflow here.
698      */
699     mptr = mline;
700     while (CurC != '\0' && CurC != RTerm) {
701         *mptr++ = CurC;
702         NextChar ();
703     }
704     *mptr = '\0';
705
706     /* Check if we got a terminator */
707     if (CurC != RTerm) {
708         /* No terminator found */
709         PPError (ERR_INCLUDE_RTERM_EXPECTED);
710         goto Done;
711     }
712
713     /* Open the include file */
714     OpenIncludeFile (mline, DirSpec);
715
716 Done:
717     /* Clear the remaining line so the next input will come from the new
718      * file (if open)
719      */
720     ClearLine ();
721 }
722
723
724
725 static void doerror (void)
726 /* Print an error */
727 {
728     SkipBlank ();
729     if (CurC == '\0') {
730         PPError (ERR_INVALID_USER_ERROR);
731     } else {
732         PPError (ERR_USER_ERROR, lptr);
733     }
734
735     /* clear rest of line */
736     ClearLine ();
737 }
738
739
740
741 /* C preprocessor. */
742
743 /* stuff used to bum the keyword dispatching stuff */
744 enum {
745     PP_DEFINE,
746     PP_ELSE,
747     PP_ENDIF,
748     PP_ERROR,
749     PP_IF,
750     PP_IFDEF,
751     PP_IFNDEF,
752     PP_INCLUDE,
753     PP_LINE,
754     PP_PRAGMA,
755     PP_UNDEF,
756     PP_ILLEGAL
757 };
758
759 static const struct tok_elt pre_toks[] = {
760     {   "define",       PP_DEFINE       },
761     {   "else",         PP_ELSE         },
762     {   "endif",        PP_ENDIF        },
763     {   "error",        PP_ERROR        },
764     {   "if",           PP_IF           },
765     {   "ifdef",        PP_IFDEF        },
766     {   "ifndef",       PP_IFNDEF       },
767     {   "include",      PP_INCLUDE      },
768     {   "line",         PP_LINE         },
769     {   "pragma",       PP_PRAGMA       },
770     {   "undef",        PP_UNDEF        },
771     {   0,              PP_ILLEGAL      }
772 };
773
774
775
776 static int searchtok (const char *sym, const struct tok_elt *toks)
777 /* Search a token in a table */
778 {
779     while (toks->toknam && strcmp (toks->toknam, sym))
780         ++toks;
781     return (toks->toknbr);
782 }
783
784
785
786 void Preprocess (void)
787 /* Preprocess a line */
788 {
789     int         Skip;
790     ident       Directive;
791
792     /* Skip white space at the beginning of the line */
793     SkipBlank ();
794
795     /* Check for stuff to skip */
796     Skip = 0;
797     while (CurC == '\0' || CurC == '#' || Skip) {
798
799         /* Check for preprocessor lines lines */
800         if (CurC == '#') {
801             NextChar ();
802             SkipBlank ();
803             if (CurC == '\0') {
804                 /* Ignore the empty preprocessor directive */
805                 continue;
806             }
807             if (!IsSym (Directive)) {
808                 PPError (ERR_CPP_DIRECTIVE_EXPECTED);
809                 ClearLine ();
810             } else {
811                 switch (searchtok (Directive, pre_toks)) {
812
813                     case PP_DEFINE:
814                         if (!Skip) {
815                             addmac ();
816                         }
817                         break;
818
819                     case PP_ELSE:
820                         if (s_ifdef[i_ifdef] & 2) {
821                             if (s_ifdef[i_ifdef] & 4) {
822                                 Skip = !Skip;
823                             }
824                             s_ifdef[i_ifdef] ^= 2;
825                         } else {
826                             PPError (ERR_UNEXPECTED_CPP_ELSE);
827                         }
828                         break;
829
830                     case PP_ENDIF:
831                         if (i_ifdef >= 0) {
832                             Skip = s_ifdef[i_ifdef--] & 1;
833                         } else {
834                             PPError (ERR_UNEXPECTED_CPP_ENDIF);
835                         }
836                         break;
837
838                     case PP_ERROR:
839                         if (!Skip) {
840                             doerror ();
841                         }
842                         break;
843
844                     case PP_IF:
845                         Skip = doiff (Skip);
846                         break;
847
848                     case PP_IFDEF:
849                         Skip = doifdef (Skip, 1);
850                         break;
851
852                     case PP_IFNDEF:
853                         Skip = doifdef (Skip, 0);
854                         break;
855
856                     case PP_INCLUDE:
857                         if (!Skip) {
858                             doinclude ();
859                         }
860                         break;
861
862                     case PP_LINE:
863                         /* Not allowed in strict ANSI mode */
864                         if (ANSI) {
865                             PPError (ERR_CPP_DIRECTIVE_EXPECTED);
866                             ClearLine ();
867                         }
868                         break;
869
870                     case PP_PRAGMA:
871                         if (!Skip) {
872                             /* Don't expand macros in this line */
873                             ExpandMacros = 0;
874                             /* #pragma is handled on the scanner level */
875                             goto Done;
876                         }
877                         break;
878
879                     case PP_UNDEF:
880                         if (!Skip) {
881                             doundef ();
882                         }
883                         break;
884
885                     default:
886                         PPError (ERR_CPP_DIRECTIVE_EXPECTED);
887                         ClearLine ();
888                 }
889             }
890
891         }
892         if (NextLine () == 0) {
893             if (i_ifdef >= 0) {
894                 PPError (ERR_CPP_ENDIF_EXPECTED);
895             }
896             return;
897         }
898         SkipBlank ();
899     }
900
901 Done:
902     xlateline ();
903     if (Verbose > 1) {
904         printf ("line: %s\n", line);
905     }
906 }
907