1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* preproc.c */ |
4 | /* */ |
5 | /* cc65 preprocessor */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2010, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <stdio.h> |
37 | #include <string.h> |
38 | #include <stdlib.h> |
39 | #include <errno.h> |
40 | |
41 | /* common */ |
42 | #include "chartype.h" |
43 | #include "check.h" |
44 | #include "inline.h" |
45 | #include "print.h" |
46 | #include "xmalloc.h" |
47 | |
48 | /* cc65 */ |
49 | #include "codegen.h" |
50 | #include "error.h" |
51 | #include "expr.h" |
52 | #include "global.h" |
53 | #include "ident.h" |
54 | #include "incpath.h" |
55 | #include "input.h" |
56 | #include "lineinfo.h" |
57 | #include "macrotab.h" |
58 | #include "preproc.h" |
59 | #include "scanner.h" |
60 | #include "standard.h" |
61 | |
62 | |
63 | |
64 | /*****************************************************************************/ |
65 | /* Data */ |
66 | /*****************************************************************************/ |
67 | |
68 | |
69 | |
70 | /* Set when the preprocessor calls expr() recursively */ |
71 | unsigned char Preprocessing = 0; |
72 | |
73 | /* Management data for #if */ |
74 | #define MAX_IFS 64 |
75 | #define IFCOND_NONE 0x00U |
76 | #define IFCOND_SKIP 0x01U |
77 | #define IFCOND_ELSE 0x02U |
78 | #define IFCOND_NEEDTERM 0x04U |
79 | static unsigned char IfStack[MAX_IFS]; |
80 | static int IfIndex = -1; |
81 | |
82 | /* Buffer for macro expansion */ |
83 | static StrBuf* MLine; |
84 | |
85 | /* Structure used when expanding macros */ |
86 | typedef struct MacroExp MacroExp; |
87 | struct MacroExp { |
88 | Collection ActualArgs; /* Actual arguments */ |
89 | StrBuf Replacement; /* Replacement with arguments substituted */ |
90 | Macro* M; /* The macro we're handling */ |
91 | }; |
92 | |
93 | |
94 | |
95 | /*****************************************************************************/ |
96 | /* Forwards */ |
97 | /*****************************************************************************/ |
98 | |
99 | |
100 | |
101 | static unsigned Pass1 (StrBuf* Source, StrBuf* Target); |
102 | /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments |
103 | ** and the "defined" operator. |
104 | */ |
105 | |
106 | static void MacroReplacement (StrBuf* Source, StrBuf* Target); |
107 | /* Perform macro replacement. */ |
108 | |
109 | |
110 | |
111 | /*****************************************************************************/ |
112 | /* Low level preprocessor token handling */ |
113 | /*****************************************************************************/ |
114 | |
115 | |
116 | |
117 | /* Types of preprocessor tokens */ |
118 | typedef enum { |
119 | PP_ILLEGAL = -1, |
120 | PP_DEFINE, |
121 | PP_ELIF, |
122 | PP_ELSE, |
123 | PP_ENDIF, |
124 | PP_ERROR, |
125 | PP_IF, |
126 | PP_IFDEF, |
127 | PP_IFNDEF, |
128 | PP_INCLUDE, |
129 | PP_LINE, |
130 | PP_PRAGMA, |
131 | PP_UNDEF, |
132 | PP_WARNING, |
133 | } pptoken_t; |
134 | |
135 | |
136 | |
137 | /* Preprocessor keyword to token mapping table */ |
138 | static const struct PPToken { |
139 | const char* Key; /* Keyword */ |
140 | pptoken_t Tok; /* Token */ |
141 | } PPTokens[] = { |
142 | { "define" , PP_DEFINE }, |
143 | { "elif" , PP_ELIF }, |
144 | { "else" , PP_ELSE }, |
145 | { "endif" , PP_ENDIF }, |
146 | { "error" , PP_ERROR }, |
147 | { "if" , PP_IF }, |
148 | { "ifdef" , PP_IFDEF }, |
149 | { "ifndef" , PP_IFNDEF }, |
150 | { "include" , PP_INCLUDE }, |
151 | { "line" , PP_LINE }, |
152 | { "pragma" , PP_PRAGMA }, |
153 | { "undef" , PP_UNDEF }, |
154 | { "warning" , PP_WARNING }, |
155 | }; |
156 | |
157 | /* Number of preprocessor tokens */ |
158 | #define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0])) |
159 | |
160 | |
161 | |
162 | static int CmpToken (const void* Key, const void* Elem) |
163 | /* Compare function for bsearch */ |
164 | { |
165 | return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key); |
166 | } |
167 | |
168 | |
169 | |
170 | static pptoken_t FindPPToken (const char* Ident) |
171 | /* Find a preprocessor token and return it. Return PP_ILLEGAL if the identifier |
172 | ** is not a valid preprocessor token. |
173 | */ |
174 | { |
175 | struct PPToken* P; |
176 | P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken); |
177 | return P? P->Tok : PP_ILLEGAL; |
178 | } |
179 | |
180 | |
181 | |
182 | /*****************************************************************************/ |
183 | /* struct MacroExp */ |
184 | /*****************************************************************************/ |
185 | |
186 | |
187 | |
188 | static MacroExp* InitMacroExp (MacroExp* E, Macro* M) |
189 | /* Initialize a MacroExp structure */ |
190 | { |
191 | InitCollection (&E->ActualArgs); |
192 | SB_Init (&E->Replacement); |
193 | E->M = M; |
194 | return E; |
195 | } |
196 | |
197 | |
198 | |
199 | static void DoneMacroExp (MacroExp* E) |
200 | /* Cleanup after use of a MacroExp structure */ |
201 | { |
202 | unsigned I; |
203 | |
204 | /* Delete the list with actual arguments */ |
205 | for (I = 0; I < CollCount (&E->ActualArgs); ++I) { |
206 | FreeStrBuf (CollAtUnchecked (&E->ActualArgs, I)); |
207 | } |
208 | DoneCollection (&E->ActualArgs); |
209 | SB_Done (&E->Replacement); |
210 | } |
211 | |
212 | |
213 | |
214 | static void ME_AppendActual (MacroExp* E, StrBuf* Arg) |
215 | /* Add a copy of Arg to the list of actual macro arguments. |
216 | ** NOTE: This function will clear Arg! |
217 | */ |
218 | { |
219 | /* Create a new string buffer */ |
220 | StrBuf* A = NewStrBuf (); |
221 | |
222 | /* Move the contents of Arg to A */ |
223 | SB_Move (A, Arg); |
224 | |
225 | /* Add A to the actual arguments */ |
226 | CollAppend (&E->ActualArgs, A); |
227 | } |
228 | |
229 | |
230 | |
231 | static StrBuf* ME_GetActual (MacroExp* E, unsigned Index) |
232 | /* Return an actual macro argument with the given index */ |
233 | { |
234 | return CollAt (&E->ActualArgs, Index); |
235 | } |
236 | |
237 | |
238 | |
239 | static int ME_ArgIsVariadic (const MacroExp* E) |
240 | /* Return true if the next actual argument we will add is a variadic one */ |
241 | { |
242 | return (E->M->Variadic && |
243 | E->M->ArgCount == (int) CollCount (&E->ActualArgs) + 1); |
244 | } |
245 | |
246 | |
247 | |
248 | /*****************************************************************************/ |
249 | /* Code */ |
250 | /*****************************************************************************/ |
251 | |
252 | |
253 | |
254 | static void Stringize (StrBuf* Source, StrBuf* Target) |
255 | /* Stringize the given string: Add double quotes at start and end and preceed |
256 | ** each occurance of " and \ by a backslash. |
257 | */ |
258 | { |
259 | char C; |
260 | |
261 | /* Add a starting quote */ |
262 | SB_AppendChar (Target, '\"'); |
263 | |
264 | /* Replace any characters inside the string may not be part of a string |
265 | ** unescaped. |
266 | */ |
267 | while ((C = SB_Get (Source)) != '\0') { |
268 | switch (C) { |
269 | case '\"': |
270 | case '\\': |
271 | SB_AppendChar (Target, '\\'); |
272 | /* FALLTHROUGH */ |
273 | default: |
274 | SB_AppendChar (Target, C); |
275 | break; |
276 | } |
277 | } |
278 | |
279 | /* Add the closing quote */ |
280 | SB_AppendChar (Target, '\"'); |
281 | } |
282 | |
283 | |
284 | |
285 | static void (void) |
286 | /* Remove an old style C comment from line. */ |
287 | { |
288 | /* Remember the current line number, so we can output better error |
289 | ** messages if the comment is not terminated in the current file. |
290 | */ |
291 | unsigned StartingLine = GetCurrentLine(); |
292 | |
293 | /* Skip the start of comment chars */ |
294 | NextChar (); |
295 | NextChar (); |
296 | |
297 | /* Skip the comment */ |
298 | while (CurC != '*' || NextC != '/') { |
299 | if (CurC == '\0') { |
300 | if (NextLine () == 0) { |
301 | PPError ("End-of-file reached in comment starting at line %u" , |
302 | StartingLine); |
303 | return; |
304 | } |
305 | } else { |
306 | if (CurC == '/' && NextC == '*') { |
307 | PPWarning ("'/*' found inside a comment" ); |
308 | } |
309 | NextChar (); |
310 | } |
311 | } |
312 | |
313 | /* Skip the end of comment chars */ |
314 | NextChar (); |
315 | NextChar (); |
316 | } |
317 | |
318 | |
319 | |
320 | static void (void) |
321 | /* Remove a new style C comment from line. */ |
322 | { |
323 | /* Beware: Because line continuation chars are handled when reading |
324 | ** lines, we may only skip until the end of the source line, which |
325 | ** may not be the same as the end of the input line. The end of the |
326 | ** source line is denoted by a lf (\n) character. |
327 | */ |
328 | do { |
329 | NextChar (); |
330 | } while (CurC != '\n' && CurC != '\0'); |
331 | if (CurC == '\n') { |
332 | NextChar (); |
333 | } |
334 | } |
335 | |
336 | |
337 | |
338 | static int SkipWhitespace (int SkipLines) |
339 | /* Skip white space in the input stream. Do also skip newlines if SkipLines |
340 | ** is true. Return zero if nothing was skipped, otherwise return a |
341 | ** value != zero. |
342 | */ |
343 | { |
344 | int Skipped = 0; |
345 | while (1) { |
346 | if (IsSpace (CurC)) { |
347 | NextChar (); |
348 | Skipped = 1; |
349 | } else if (CurC == '\0' && SkipLines) { |
350 | /* End of line, read next */ |
351 | if (NextLine () != 0) { |
352 | Skipped = 1; |
353 | } else { |
354 | /* End of input */ |
355 | break; |
356 | } |
357 | } else { |
358 | /* No more white space */ |
359 | break; |
360 | } |
361 | } |
362 | return Skipped; |
363 | } |
364 | |
365 | |
366 | |
367 | static void CopyQuotedString (StrBuf* Target) |
368 | /* Copy a single or double quoted string from the input to Target. */ |
369 | { |
370 | /* Remember the quote character, copy it to the target buffer and skip it */ |
371 | char Quote = CurC; |
372 | SB_AppendChar (Target, CurC); |
373 | NextChar (); |
374 | |
375 | /* Copy the characters inside the string */ |
376 | while (CurC != '\0' && CurC != Quote) { |
377 | /* Keep an escaped char */ |
378 | if (CurC == '\\') { |
379 | SB_AppendChar (Target, CurC); |
380 | NextChar (); |
381 | } |
382 | /* Copy the character */ |
383 | SB_AppendChar (Target, CurC); |
384 | NextChar (); |
385 | } |
386 | |
387 | /* If we had a terminating quote, copy it */ |
388 | if (CurC != '\0') { |
389 | SB_AppendChar (Target, CurC); |
390 | NextChar (); |
391 | } |
392 | } |
393 | |
394 | |
395 | |
396 | /*****************************************************************************/ |
397 | /* Macro stuff */ |
398 | /*****************************************************************************/ |
399 | |
400 | |
401 | |
402 | static int MacName (char* Ident) |
403 | /* Get a macro symbol name into Ident. If we have an error, print a |
404 | ** diagnostic message and clear the line. |
405 | */ |
406 | { |
407 | if (IsSym (Ident) == 0) { |
408 | PPError ("Identifier expected" ); |
409 | ClearLine (); |
410 | return 0; |
411 | } else { |
412 | return 1; |
413 | } |
414 | } |
415 | |
416 | |
417 | |
418 | static void ReadMacroArgs (MacroExp* E) |
419 | /* Identify the arguments to a macro call */ |
420 | { |
421 | unsigned Parens; /* Number of open parenthesis */ |
422 | StrBuf Arg = STATIC_STRBUF_INITIALIZER; |
423 | |
424 | /* Read the actual macro arguments */ |
425 | Parens = 0; |
426 | while (1) { |
427 | if (CurC == '(') { |
428 | |
429 | /* Nested parenthesis */ |
430 | SB_AppendChar (&Arg, CurC); |
431 | NextChar (); |
432 | ++Parens; |
433 | |
434 | } else if (IsQuote (CurC)) { |
435 | |
436 | /* Quoted string - just copy */ |
437 | CopyQuotedString (&Arg); |
438 | |
439 | } else if (CurC == ',' || CurC == ')') { |
440 | |
441 | if (Parens) { |
442 | /* Comma or right paren inside nested parenthesis */ |
443 | if (CurC == ')') { |
444 | --Parens; |
445 | } |
446 | SB_AppendChar (&Arg, CurC); |
447 | NextChar (); |
448 | } else if (CurC == ',' && ME_ArgIsVariadic (E)) { |
449 | /* It's a comma, but we're inside a variadic macro argument, so |
450 | ** just copy it and proceed. |
451 | */ |
452 | SB_AppendChar (&Arg, CurC); |
453 | NextChar (); |
454 | } else { |
455 | /* End of actual argument. Remove whitespace from the end. */ |
456 | while (IsSpace (SB_LookAtLast (&Arg))) { |
457 | SB_Drop (&Arg, 1); |
458 | } |
459 | |
460 | /* If this is not the single empty argument for a macro with |
461 | ** an empty argument list, remember it. |
462 | */ |
463 | if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) { |
464 | ME_AppendActual (E, &Arg); |
465 | } |
466 | |
467 | /* Check for end of macro param list */ |
468 | if (CurC == ')') { |
469 | NextChar (); |
470 | break; |
471 | } |
472 | |
473 | /* Start the next param */ |
474 | NextChar (); |
475 | SB_Clear (&Arg); |
476 | } |
477 | } else if (SkipWhitespace (1)) { |
478 | /* Squeeze runs of blanks within an arg */ |
479 | if (SB_NotEmpty (&Arg)) { |
480 | SB_AppendChar (&Arg, ' '); |
481 | } |
482 | } else if (CurC == '/' && NextC == '*') { |
483 | if (SB_NotEmpty (&Arg)) { |
484 | SB_AppendChar (&Arg, ' '); |
485 | } |
486 | OldStyleComment (); |
487 | } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { |
488 | if (SB_NotEmpty (&Arg)) { |
489 | SB_AppendChar (&Arg, ' '); |
490 | } |
491 | NewStyleComment (); |
492 | } else if (CurC == '\0') { |
493 | /* End of input inside macro argument list */ |
494 | PPError ("Unterminated argument list invoking macro '%s'" , E->M->Name); |
495 | |
496 | ClearLine (); |
497 | break; |
498 | } else { |
499 | /* Just copy the character */ |
500 | SB_AppendChar (&Arg, CurC); |
501 | NextChar (); |
502 | } |
503 | } |
504 | |
505 | /* Deallocate string buf resources */ |
506 | SB_Done (&Arg); |
507 | } |
508 | |
509 | |
510 | |
511 | static void MacroArgSubst (MacroExp* E) |
512 | /* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */ |
513 | { |
514 | ident Ident; |
515 | int ArgIdx; |
516 | StrBuf* OldSource; |
517 | StrBuf* Arg; |
518 | int HaveSpace; |
519 | |
520 | |
521 | /* Remember the current input and switch to the macro replacement. */ |
522 | int OldIndex = SB_GetIndex (&E->M->Replacement); |
523 | SB_Reset (&E->M->Replacement); |
524 | OldSource = InitLine (&E->M->Replacement); |
525 | |
526 | /* Argument handling loop */ |
527 | while (CurC != '\0') { |
528 | |
529 | /* If we have an identifier, check if it's a macro */ |
530 | if (IsSym (Ident)) { |
531 | |
532 | /* Check if it's a macro argument */ |
533 | if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { |
534 | |
535 | /* A macro argument. Get the corresponding actual argument. */ |
536 | Arg = ME_GetActual (E, ArgIdx); |
537 | |
538 | /* Copy any following whitespace */ |
539 | HaveSpace = SkipWhitespace (0); |
540 | |
541 | /* If a ## operator follows, we have to insert the actual |
542 | ** argument as is, otherwise it must be macro replaced. |
543 | */ |
544 | if (CurC == '#' && NextC == '#') { |
545 | |
546 | /* ### Add placemarker if necessary */ |
547 | SB_Append (&E->Replacement, Arg); |
548 | |
549 | } else { |
550 | |
551 | /* Replace the formal argument by a macro replaced copy |
552 | ** of the actual. |
553 | */ |
554 | SB_Reset (Arg); |
555 | MacroReplacement (Arg, &E->Replacement); |
556 | |
557 | /* If we skipped whitespace before, re-add it now */ |
558 | if (HaveSpace) { |
559 | SB_AppendChar (&E->Replacement, ' '); |
560 | } |
561 | } |
562 | |
563 | |
564 | } else { |
565 | |
566 | /* An identifier, keep it */ |
567 | SB_AppendStr (&E->Replacement, Ident); |
568 | |
569 | } |
570 | |
571 | } else if (CurC == '#' && NextC == '#') { |
572 | |
573 | /* ## operator. */ |
574 | NextChar (); |
575 | NextChar (); |
576 | SkipWhitespace (0); |
577 | |
578 | /* Since we need to concatenate the token sequences, remove |
579 | ** any whitespace that was added to target, since it must come |
580 | ** from the input. |
581 | */ |
582 | while (IsSpace (SB_LookAtLast (&E->Replacement))) { |
583 | SB_Drop (&E->Replacement, 1); |
584 | } |
585 | |
586 | /* If the next token is an identifier which is a macro argument, |
587 | ** replace it, otherwise do nothing. |
588 | */ |
589 | if (IsSym (Ident)) { |
590 | |
591 | /* Check if it's a macro argument */ |
592 | if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { |
593 | |
594 | /* Get the corresponding actual argument and add it. */ |
595 | SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx)); |
596 | |
597 | } else { |
598 | |
599 | /* Just an ordinary identifier - add as is */ |
600 | SB_AppendStr (&E->Replacement, Ident); |
601 | |
602 | } |
603 | } |
604 | |
605 | } else if (CurC == '#' && E->M->ArgCount >= 0) { |
606 | |
607 | /* A # operator within a macro expansion of a function like |
608 | ** macro. Read the following identifier and check if it's a |
609 | ** macro parameter. |
610 | */ |
611 | NextChar (); |
612 | SkipWhitespace (0); |
613 | if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) { |
614 | PPError ("'#' is not followed by a macro parameter" ); |
615 | } else { |
616 | /* Make a valid string from Replacement */ |
617 | Arg = ME_GetActual (E, ArgIdx); |
618 | SB_Reset (Arg); |
619 | Stringize (Arg, &E->Replacement); |
620 | } |
621 | |
622 | } else if (IsQuote (CurC)) { |
623 | CopyQuotedString (&E->Replacement); |
624 | } else { |
625 | SB_AppendChar (&E->Replacement, CurC); |
626 | NextChar (); |
627 | } |
628 | } |
629 | |
630 | #if 0 |
631 | /* Remove whitespace from the end of the line */ |
632 | while (IsSpace (SB_LookAtLast (&E->Replacement))) { |
633 | SB_Drop (&E->Replacement, 1); |
634 | } |
635 | #endif |
636 | |
637 | /* Switch back the input */ |
638 | InitLine (OldSource); |
639 | SB_SetIndex (&E->M->Replacement, OldIndex); |
640 | } |
641 | |
642 | |
643 | |
644 | static void MacroCall (StrBuf* Target, Macro* M) |
645 | /* Process a function like macro */ |
646 | { |
647 | MacroExp E; |
648 | |
649 | /* Eat the left paren */ |
650 | NextChar (); |
651 | |
652 | /* Initialize our MacroExp structure */ |
653 | InitMacroExp (&E, M); |
654 | |
655 | /* Read the actual macro arguments */ |
656 | ReadMacroArgs (&E); |
657 | |
658 | /* Compare formal and actual argument count */ |
659 | if (CollCount (&E.ActualArgs) != (unsigned) M->ArgCount) { |
660 | |
661 | StrBuf Arg = STATIC_STRBUF_INITIALIZER; |
662 | |
663 | /* Argument count mismatch */ |
664 | PPError ("Macro argument count mismatch" ); |
665 | |
666 | /* Be sure to make enough empty arguments available */ |
667 | while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) { |
668 | ME_AppendActual (&E, &Arg); |
669 | } |
670 | } |
671 | |
672 | /* Replace macro arguments handling the # and ## operators */ |
673 | MacroArgSubst (&E); |
674 | |
675 | /* Do macro replacement on the macro that already has the parameters |
676 | ** substituted. |
677 | */ |
678 | M->Expanding = 1; |
679 | MacroReplacement (&E.Replacement, Target); |
680 | M->Expanding = 0; |
681 | |
682 | /* Free memory allocated for the macro expansion structure */ |
683 | DoneMacroExp (&E); |
684 | } |
685 | |
686 | |
687 | |
688 | static void ExpandMacro (StrBuf* Target, Macro* M) |
689 | /* Expand a macro into Target */ |
690 | { |
691 | #if 0 |
692 | static unsigned V = 0; |
693 | printf ("Expanding %s(%u)\n" , M->Name, ++V); |
694 | #endif |
695 | |
696 | /* Check if this is a function like macro */ |
697 | if (M->ArgCount >= 0) { |
698 | |
699 | int Whitespace = SkipWhitespace (1); |
700 | if (CurC != '(') { |
701 | /* Function like macro but no parameter list */ |
702 | SB_AppendStr (Target, M->Name); |
703 | if (Whitespace) { |
704 | SB_AppendChar (Target, ' '); |
705 | } |
706 | } else { |
707 | /* Function like macro */ |
708 | MacroCall (Target, M); |
709 | } |
710 | |
711 | } else { |
712 | |
713 | MacroExp E; |
714 | InitMacroExp (&E, M); |
715 | |
716 | /* Handle # and ## operators for object like macros */ |
717 | MacroArgSubst (&E); |
718 | |
719 | /* Do macro replacement on the macro that already has the parameters |
720 | ** substituted. |
721 | */ |
722 | M->Expanding = 1; |
723 | MacroReplacement (&E.Replacement, Target); |
724 | M->Expanding = 0; |
725 | |
726 | /* Free memory allocated for the macro expansion structure */ |
727 | DoneMacroExp (&E); |
728 | |
729 | } |
730 | #if 0 |
731 | printf ("Done with %s(%u)\n" , M->Name, V--); |
732 | #endif |
733 | } |
734 | |
735 | |
736 | |
737 | static void DefineMacro (void) |
738 | /* Handle a macro definition. */ |
739 | { |
740 | ident Ident; |
741 | Macro* M; |
742 | Macro* Existing; |
743 | int C89; |
744 | |
745 | /* Read the macro name */ |
746 | SkipWhitespace (0); |
747 | if (!MacName (Ident)) { |
748 | return; |
749 | } |
750 | |
751 | /* Remember if we're in C89 mode */ |
752 | C89 = (IS_Get (&Standard) == STD_C89); |
753 | |
754 | /* Get an existing macro definition with this name */ |
755 | Existing = FindMacro (Ident); |
756 | |
757 | /* Create a new macro definition */ |
758 | M = NewMacro (Ident); |
759 | |
760 | /* Check if this is a function like macro */ |
761 | if (CurC == '(') { |
762 | |
763 | /* Skip the left paren */ |
764 | NextChar (); |
765 | |
766 | /* Set the marker that this is a function like macro */ |
767 | M->ArgCount = 0; |
768 | |
769 | /* Read the formal parameter list */ |
770 | while (1) { |
771 | |
772 | /* Skip white space and check for end of parameter list */ |
773 | SkipWhitespace (0); |
774 | if (CurC == ')') { |
775 | break; |
776 | } |
777 | |
778 | /* The next token must be either an identifier, or - if not in |
779 | ** C89 mode - the ellipsis. |
780 | */ |
781 | if (!C89 && CurC == '.') { |
782 | /* Ellipsis */ |
783 | NextChar (); |
784 | if (CurC != '.' || NextC != '.') { |
785 | PPError ("'...' expected" ); |
786 | ClearLine (); |
787 | return; |
788 | } |
789 | NextChar (); |
790 | NextChar (); |
791 | |
792 | /* Remember that the macro is variadic and use __VA_ARGS__ as |
793 | ** the argument name. |
794 | */ |
795 | AddMacroArg (M, "__VA_ARGS__" ); |
796 | M->Variadic = 1; |
797 | |
798 | } else { |
799 | /* Must be macro argument name */ |
800 | if (MacName (Ident) == 0) { |
801 | return; |
802 | } |
803 | |
804 | /* __VA_ARGS__ is only allowed in C89 mode */ |
805 | if (!C89 && strcmp (Ident, "__VA_ARGS__" ) == 0) { |
806 | PPWarning ("'__VA_ARGS__' can only appear in the expansion " |
807 | "of a C99 variadic macro" ); |
808 | } |
809 | |
810 | /* Add the macro argument */ |
811 | AddMacroArg (M, Ident); |
812 | } |
813 | |
814 | /* If we had an ellipsis, or the next char is not a comma, we've |
815 | ** reached the end of the macro argument list. |
816 | */ |
817 | SkipWhitespace (0); |
818 | if (M->Variadic || CurC != ',') { |
819 | break; |
820 | } |
821 | NextChar (); |
822 | } |
823 | |
824 | /* Check for a right paren and eat it if we find one */ |
825 | if (CurC != ')') { |
826 | PPError ("')' expected" ); |
827 | ClearLine (); |
828 | return; |
829 | } |
830 | NextChar (); |
831 | } |
832 | |
833 | /* Skip whitespace before the macro replacement */ |
834 | SkipWhitespace (0); |
835 | |
836 | /* Insert the macro into the macro table and allocate the ActualArgs array */ |
837 | InsertMacro (M); |
838 | |
839 | /* Remove whitespace and comments from the line, store the preprocessed |
840 | ** line into the macro replacement buffer. |
841 | */ |
842 | Pass1 (Line, &M->Replacement); |
843 | |
844 | /* Remove whitespace from the end of the line */ |
845 | while (IsSpace (SB_LookAtLast (&M->Replacement))) { |
846 | SB_Drop (&M->Replacement, 1); |
847 | } |
848 | #if 0 |
849 | printf ("%s: <%.*s>\n" , M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement)); |
850 | #endif |
851 | |
852 | /* If we have an existing macro, check if the redefinition is identical. |
853 | ** Print a diagnostic if not. |
854 | */ |
855 | if (Existing && MacroCmp (M, Existing) != 0) { |
856 | PPError ("Macro redefinition is not identical" ); |
857 | } |
858 | } |
859 | |
860 | |
861 | |
862 | /*****************************************************************************/ |
863 | /* Preprocessing */ |
864 | /*****************************************************************************/ |
865 | |
866 | |
867 | |
868 | static unsigned Pass1 (StrBuf* Source, StrBuf* Target) |
869 | /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments |
870 | ** and the "defined" operator. |
871 | */ |
872 | { |
873 | unsigned IdentCount; |
874 | ident Ident; |
875 | int HaveParen; |
876 | |
877 | /* Switch to the new input source */ |
878 | StrBuf* OldSource = InitLine (Source); |
879 | |
880 | /* Loop removing ws and comments */ |
881 | IdentCount = 0; |
882 | while (CurC != '\0') { |
883 | if (SkipWhitespace (0)) { |
884 | /* Squeeze runs of blanks */ |
885 | if (!IsSpace (SB_LookAtLast (Target))) { |
886 | SB_AppendChar (Target, ' '); |
887 | } |
888 | } else if (IsSym (Ident)) { |
889 | if (Preprocessing && strcmp (Ident, "defined" ) == 0) { |
890 | /* Handle the "defined" operator */ |
891 | SkipWhitespace (0); |
892 | HaveParen = 0; |
893 | if (CurC == '(') { |
894 | HaveParen = 1; |
895 | NextChar (); |
896 | SkipWhitespace (0); |
897 | } |
898 | if (IsSym (Ident)) { |
899 | SB_AppendChar (Target, IsMacro (Ident)? '1' : '0'); |
900 | if (HaveParen) { |
901 | SkipWhitespace (0); |
902 | if (CurC != ')') { |
903 | PPError ("')' expected" ); |
904 | } else { |
905 | NextChar (); |
906 | } |
907 | } |
908 | } else { |
909 | PPError ("Identifier expected" ); |
910 | SB_AppendChar (Target, '0'); |
911 | } |
912 | } else { |
913 | ++IdentCount; |
914 | SB_AppendStr (Target, Ident); |
915 | } |
916 | } else if (IsQuote (CurC)) { |
917 | CopyQuotedString (Target); |
918 | } else if (CurC == '/' && NextC == '*') { |
919 | if (!IsSpace (SB_LookAtLast (Target))) { |
920 | SB_AppendChar (Target, ' '); |
921 | } |
922 | OldStyleComment (); |
923 | } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { |
924 | if (!IsSpace (SB_LookAtLast (Target))) { |
925 | SB_AppendChar (Target, ' '); |
926 | } |
927 | NewStyleComment (); |
928 | } else { |
929 | SB_AppendChar (Target, CurC); |
930 | NextChar (); |
931 | } |
932 | } |
933 | |
934 | /* Switch back to the old source */ |
935 | InitLine (OldSource); |
936 | |
937 | /* Return the number of identifiers found in the line */ |
938 | return IdentCount; |
939 | } |
940 | |
941 | |
942 | |
943 | static void MacroReplacement (StrBuf* Source, StrBuf* Target) |
944 | /* Perform macro replacement. */ |
945 | { |
946 | ident Ident; |
947 | Macro* M; |
948 | |
949 | /* Remember the current input and switch to Source */ |
950 | StrBuf* OldSource = InitLine (Source); |
951 | |
952 | /* Loop substituting macros */ |
953 | while (CurC != '\0') { |
954 | /* If we have an identifier, check if it's a macro */ |
955 | if (IsSym (Ident)) { |
956 | /* Check if it's a macro */ |
957 | if ((M = FindMacro (Ident)) != 0 && !M->Expanding) { |
958 | /* It's a macro, expand it */ |
959 | ExpandMacro (Target, M); |
960 | } else { |
961 | /* An identifier, keep it */ |
962 | SB_AppendStr (Target, Ident); |
963 | } |
964 | } else if (IsQuote (CurC)) { |
965 | CopyQuotedString (Target); |
966 | } else if (IsSpace (CurC)) { |
967 | if (!IsSpace (SB_LookAtLast (Target))) { |
968 | SB_AppendChar (Target, CurC); |
969 | } |
970 | NextChar (); |
971 | } else { |
972 | SB_AppendChar (Target, CurC); |
973 | NextChar (); |
974 | } |
975 | } |
976 | |
977 | /* Switch back the input */ |
978 | InitLine (OldSource); |
979 | } |
980 | |
981 | |
982 | |
983 | static void PreprocessLine (void) |
984 | /* Translate one line. */ |
985 | { |
986 | /* Trim whitespace and remove comments. The function returns the number of |
987 | ** identifiers found. If there were any, we will have to check for macros. |
988 | */ |
989 | SB_Clear (MLine); |
990 | if (Pass1 (Line, MLine) > 0) { |
991 | MLine = InitLine (MLine); |
992 | SB_Reset (Line); |
993 | SB_Clear (MLine); |
994 | MacroReplacement (Line, MLine); |
995 | } |
996 | |
997 | /* Read from the new line */ |
998 | SB_Reset (MLine); |
999 | MLine = InitLine (MLine); |
1000 | } |
1001 | |
1002 | |
1003 | |
1004 | static int PushIf (int Skip, int Invert, int Cond) |
1005 | /* Push a new if level onto the if stack */ |
1006 | { |
1007 | /* Check for an overflow of the if stack */ |
1008 | if (IfIndex >= MAX_IFS-1) { |
1009 | PPError ("Too many nested #if clauses" ); |
1010 | return 1; |
1011 | } |
1012 | |
1013 | /* Push the #if condition */ |
1014 | ++IfIndex; |
1015 | if (Skip) { |
1016 | IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM; |
1017 | return 1; |
1018 | } else { |
1019 | IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM; |
1020 | return (Invert ^ Cond); |
1021 | } |
1022 | } |
1023 | |
1024 | |
1025 | |
1026 | static void DoError (void) |
1027 | /* Print an error */ |
1028 | { |
1029 | SkipWhitespace (0); |
1030 | if (CurC == '\0') { |
1031 | PPError ("Invalid #error directive" ); |
1032 | } else { |
1033 | PPError ("#error: %s" , SB_GetConstBuf (Line) + SB_GetIndex (Line)); |
1034 | } |
1035 | |
1036 | /* Clear the rest of line */ |
1037 | ClearLine (); |
1038 | } |
1039 | |
1040 | |
1041 | |
1042 | static int DoIf (int Skip) |
1043 | /* Process #if directive */ |
1044 | { |
1045 | ExprDesc Expr; |
1046 | |
1047 | /* We're about to abuse the compiler expression parser to evaluate the |
1048 | ** #if expression. Save the current tokens to come back here later. |
1049 | ** NOTE: Yes, this is a hack, but it saves a complete separate expression |
1050 | ** evaluation for the preprocessor. |
1051 | */ |
1052 | Token SavedCurTok = CurTok; |
1053 | Token SavedNextTok = NextTok; |
1054 | |
1055 | /* Make sure the line infos for the tokens won't get removed */ |
1056 | if (SavedCurTok.LI) { |
1057 | UseLineInfo (SavedCurTok.LI); |
1058 | } |
1059 | if (SavedNextTok.LI) { |
1060 | UseLineInfo (SavedNextTok.LI); |
1061 | } |
1062 | |
1063 | /* Switch into special preprocessing mode */ |
1064 | Preprocessing = 1; |
1065 | |
1066 | /* Expand macros in this line */ |
1067 | PreprocessLine (); |
1068 | |
1069 | /* Add two semicolons as sentinels to the line, so the following |
1070 | ** expression evaluation will eat these two tokens but nothing from |
1071 | ** the following line. |
1072 | */ |
1073 | SB_AppendStr (Line, ";;" ); |
1074 | SB_Terminate (Line); |
1075 | |
1076 | /* Load CurTok and NextTok with tokens from the new input */ |
1077 | NextToken (); |
1078 | NextToken (); |
1079 | |
1080 | /* Call the expression parser */ |
1081 | ConstExpr (hie1, &Expr); |
1082 | |
1083 | /* End preprocessing mode */ |
1084 | Preprocessing = 0; |
1085 | |
1086 | /* Reset the old tokens */ |
1087 | CurTok = SavedCurTok; |
1088 | NextTok = SavedNextTok; |
1089 | |
1090 | /* Set the #if condition according to the expression result */ |
1091 | return PushIf (Skip, 1, Expr.IVal != 0); |
1092 | } |
1093 | |
1094 | |
1095 | |
1096 | static int DoIfDef (int skip, int flag) |
1097 | /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */ |
1098 | { |
1099 | ident Ident; |
1100 | |
1101 | SkipWhitespace (0); |
1102 | if (MacName (Ident) == 0) { |
1103 | return 0; |
1104 | } else { |
1105 | return PushIf (skip, flag, IsMacro(Ident)); |
1106 | } |
1107 | } |
1108 | |
1109 | |
1110 | |
1111 | static void DoInclude (void) |
1112 | /* Open an include file. */ |
1113 | { |
1114 | char RTerm; |
1115 | InputType IT; |
1116 | StrBuf Filename = STATIC_STRBUF_INITIALIZER; |
1117 | |
1118 | |
1119 | /* Preprocess the remainder of the line */ |
1120 | PreprocessLine (); |
1121 | |
1122 | /* Skip blanks */ |
1123 | SkipWhitespace (0); |
1124 | |
1125 | /* Get the next char and check for a valid file name terminator. Setup |
1126 | ** the include directory spec (SYS/USR) by looking at the terminator. |
1127 | */ |
1128 | switch (CurC) { |
1129 | |
1130 | case '\"': |
1131 | RTerm = '\"'; |
1132 | IT = IT_USRINC; |
1133 | break; |
1134 | |
1135 | case '<': |
1136 | RTerm = '>'; |
1137 | IT = IT_SYSINC; |
1138 | break; |
1139 | |
1140 | default: |
1141 | PPError ("'\"' or '<' expected" ); |
1142 | goto Done; |
1143 | } |
1144 | NextChar (); |
1145 | |
1146 | /* Get a copy of the filename */ |
1147 | while (CurC != '\0' && CurC != RTerm) { |
1148 | SB_AppendChar (&Filename, CurC); |
1149 | NextChar (); |
1150 | } |
1151 | SB_Terminate (&Filename); |
1152 | |
1153 | /* Check if we got a terminator */ |
1154 | if (CurC == RTerm) { |
1155 | /* Open the include file */ |
1156 | OpenIncludeFile (SB_GetConstBuf (&Filename), IT); |
1157 | } else if (CurC == '\0') { |
1158 | /* No terminator found */ |
1159 | PPError ("#include expects \"FILENAME\" or <FILENAME>" ); |
1160 | } |
1161 | |
1162 | Done: |
1163 | /* Free the allocated filename data */ |
1164 | SB_Done (&Filename); |
1165 | |
1166 | /* Clear the remaining line so the next input will come from the new |
1167 | ** file (if open) |
1168 | */ |
1169 | ClearLine (); |
1170 | } |
1171 | |
1172 | |
1173 | |
1174 | static void DoPragma (void) |
1175 | /* Handle a #pragma line by converting the #pragma preprocessor directive into |
1176 | ** the _Pragma() compiler operator. |
1177 | */ |
1178 | { |
1179 | /* Skip blanks following the #pragma directive */ |
1180 | SkipWhitespace (0); |
1181 | |
1182 | /* Copy the remainder of the line into MLine removing comments and ws */ |
1183 | SB_Clear (MLine); |
1184 | Pass1 (Line, MLine); |
1185 | |
1186 | /* Convert the directive into the operator */ |
1187 | SB_CopyStr (Line, "_Pragma (" ); |
1188 | SB_Reset (MLine); |
1189 | Stringize (MLine, Line); |
1190 | SB_AppendChar (Line, ')'); |
1191 | |
1192 | /* Initialize reading from line */ |
1193 | SB_Reset (Line); |
1194 | InitLine (Line); |
1195 | } |
1196 | |
1197 | |
1198 | |
1199 | static void DoUndef (void) |
1200 | /* Process the #undef directive */ |
1201 | { |
1202 | ident Ident; |
1203 | |
1204 | SkipWhitespace (0); |
1205 | if (MacName (Ident)) { |
1206 | UndefineMacro (Ident); |
1207 | } |
1208 | } |
1209 | |
1210 | |
1211 | |
1212 | static void DoWarning (void) |
1213 | /* Print a warning */ |
1214 | { |
1215 | SkipWhitespace (0); |
1216 | if (CurC == '\0') { |
1217 | PPError ("Invalid #warning directive" ); |
1218 | } else { |
1219 | PPWarning ("#warning: %s" , SB_GetConstBuf (Line) + SB_GetIndex (Line)); |
1220 | } |
1221 | |
1222 | /* Clear the rest of line */ |
1223 | ClearLine (); |
1224 | } |
1225 | |
1226 | |
1227 | |
1228 | void Preprocess (void) |
1229 | /* Preprocess a line */ |
1230 | { |
1231 | int Skip; |
1232 | ident Directive; |
1233 | |
1234 | /* Create the output buffer if we don't already have one */ |
1235 | if (MLine == 0) { |
1236 | MLine = NewStrBuf (); |
1237 | } |
1238 | |
1239 | /* Skip white space at the beginning of the line */ |
1240 | SkipWhitespace (0); |
1241 | |
1242 | /* Check for stuff to skip */ |
1243 | Skip = 0; |
1244 | while (CurC == '\0' || CurC == '#' || Skip) { |
1245 | |
1246 | /* Check for preprocessor lines lines */ |
1247 | if (CurC == '#') { |
1248 | NextChar (); |
1249 | SkipWhitespace (0); |
1250 | if (CurC == '\0') { |
1251 | /* Ignore the empty preprocessor directive */ |
1252 | continue; |
1253 | } |
1254 | if (!IsSym (Directive)) { |
1255 | PPError ("Preprocessor directive expected" ); |
1256 | ClearLine (); |
1257 | } else { |
1258 | switch (FindPPToken (Directive)) { |
1259 | |
1260 | case PP_DEFINE: |
1261 | if (!Skip) { |
1262 | DefineMacro (); |
1263 | } |
1264 | break; |
1265 | |
1266 | case PP_ELIF: |
1267 | if (IfIndex >= 0) { |
1268 | if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { |
1269 | |
1270 | /* Handle as #else/#if combination */ |
1271 | if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { |
1272 | Skip = !Skip; |
1273 | } |
1274 | IfStack[IfIndex] |= IFCOND_ELSE; |
1275 | Skip = DoIf (Skip); |
1276 | |
1277 | /* #elif doesn't need a terminator */ |
1278 | IfStack[IfIndex] &= ~IFCOND_NEEDTERM; |
1279 | } else { |
1280 | PPError ("Duplicate #else/#elif" ); |
1281 | } |
1282 | } else { |
1283 | PPError ("Unexpected #elif" ); |
1284 | } |
1285 | break; |
1286 | |
1287 | case PP_ELSE: |
1288 | if (IfIndex >= 0) { |
1289 | if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { |
1290 | if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { |
1291 | Skip = !Skip; |
1292 | } |
1293 | IfStack[IfIndex] |= IFCOND_ELSE; |
1294 | } else { |
1295 | PPError ("Duplicate #else" ); |
1296 | } |
1297 | } else { |
1298 | PPError ("Unexpected '#else'" ); |
1299 | } |
1300 | break; |
1301 | |
1302 | case PP_ENDIF: |
1303 | if (IfIndex >= 0) { |
1304 | /* Remove any clauses on top of stack that do not |
1305 | ** need a terminating #endif. |
1306 | */ |
1307 | while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) { |
1308 | --IfIndex; |
1309 | } |
1310 | |
1311 | /* Stack may not be empty here or something is wrong */ |
1312 | CHECK (IfIndex >= 0); |
1313 | |
1314 | /* Remove the clause that needs a terminator */ |
1315 | Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0; |
1316 | } else { |
1317 | PPError ("Unexpected '#endif'" ); |
1318 | } |
1319 | break; |
1320 | |
1321 | case PP_ERROR: |
1322 | if (!Skip) { |
1323 | DoError (); |
1324 | } |
1325 | break; |
1326 | |
1327 | case PP_IF: |
1328 | Skip = DoIf (Skip); |
1329 | break; |
1330 | |
1331 | case PP_IFDEF: |
1332 | Skip = DoIfDef (Skip, 1); |
1333 | break; |
1334 | |
1335 | case PP_IFNDEF: |
1336 | Skip = DoIfDef (Skip, 0); |
1337 | break; |
1338 | |
1339 | case PP_INCLUDE: |
1340 | if (!Skip) { |
1341 | DoInclude (); |
1342 | } |
1343 | break; |
1344 | |
1345 | case PP_LINE: |
1346 | /* Should do something in C99 at least, but we ignore it */ |
1347 | if (!Skip) { |
1348 | ClearLine (); |
1349 | } |
1350 | break; |
1351 | |
1352 | case PP_PRAGMA: |
1353 | if (!Skip) { |
1354 | DoPragma (); |
1355 | goto Done; |
1356 | } |
1357 | break; |
1358 | |
1359 | case PP_UNDEF: |
1360 | if (!Skip) { |
1361 | DoUndef (); |
1362 | } |
1363 | break; |
1364 | |
1365 | case PP_WARNING: |
1366 | /* #warning is a non standard extension */ |
1367 | if (IS_Get (&Standard) > STD_C99) { |
1368 | if (!Skip) { |
1369 | DoWarning (); |
1370 | } |
1371 | } else { |
1372 | if (!Skip) { |
1373 | PPError ("Preprocessor directive expected" ); |
1374 | } |
1375 | ClearLine (); |
1376 | } |
1377 | break; |
1378 | |
1379 | default: |
1380 | if (!Skip) { |
1381 | PPError ("Preprocessor directive expected" ); |
1382 | } |
1383 | ClearLine (); |
1384 | } |
1385 | } |
1386 | |
1387 | } |
1388 | if (NextLine () == 0) { |
1389 | if (IfIndex >= 0) { |
1390 | PPError ("'#endif' expected" ); |
1391 | } |
1392 | return; |
1393 | } |
1394 | SkipWhitespace (0); |
1395 | } |
1396 | |
1397 | PreprocessLine (); |
1398 | |
1399 | Done: |
1400 | if (Verbosity > 1 && SB_NotEmpty (Line)) { |
1401 | printf ("%s(%u): %.*s\n" , GetCurrentFile (), GetCurrentLine (), |
1402 | (int) SB_GetLen (Line), SB_GetConstBuf (Line)); |
1403 | } |
1404 | } |
1405 | |