1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* pragma.c */ |
4 | /* */ |
5 | /* Pragma handling for the cc65 C compiler */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2011, 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 <stdlib.h> |
37 | #include <string.h> |
38 | |
39 | /* common */ |
40 | #include "chartype.h" |
41 | #include "segnames.h" |
42 | #include "tgttrans.h" |
43 | |
44 | /* cc65 */ |
45 | #include "codegen.h" |
46 | #include "error.h" |
47 | #include "expr.h" |
48 | #include "global.h" |
49 | #include "litpool.h" |
50 | #include "scanner.h" |
51 | #include "scanstrbuf.h" |
52 | #include "symtab.h" |
53 | #include "pragma.h" |
54 | #include "wrappedcall.h" |
55 | |
56 | |
57 | |
58 | /*****************************************************************************/ |
59 | /* data */ |
60 | /*****************************************************************************/ |
61 | |
62 | |
63 | |
64 | /* Tokens for the #pragmas */ |
65 | typedef enum { |
66 | PRAGMA_ILLEGAL = -1, |
67 | PRAGMA_ALIGN, |
68 | PRAGMA_ALLOW_EAGER_INLINE, |
69 | PRAGMA_BSS_NAME, |
70 | PRAGMA_BSSSEG, /* obsolete */ |
71 | PRAGMA_CHARMAP, |
72 | PRAGMA_CHECK_STACK, |
73 | PRAGMA_CHECKSTACK, /* obsolete */ |
74 | PRAGMA_CODE_NAME, |
75 | PRAGMA_CODESEG, /* obsolete */ |
76 | PRAGMA_CODESIZE, |
77 | PRAGMA_DATA_NAME, |
78 | PRAGMA_DATASEG, /* obsolete */ |
79 | PRAGMA_INLINE_STDFUNCS, |
80 | PRAGMA_LOCAL_STRINGS, |
81 | PRAGMA_MESSAGE, |
82 | PRAGMA_OPTIMIZE, |
83 | PRAGMA_REGISTER_VARS, |
84 | PRAGMA_REGVARADDR, |
85 | PRAGMA_REGVARS, /* obsolete */ |
86 | PRAGMA_RODATA_NAME, |
87 | PRAGMA_RODATASEG, /* obsolete */ |
88 | PRAGMA_SIGNED_CHARS, |
89 | PRAGMA_SIGNEDCHARS, /* obsolete */ |
90 | PRAGMA_STATIC_LOCALS, |
91 | PRAGMA_STATICLOCALS, /* obsolete */ |
92 | PRAGMA_WARN, |
93 | PRAGMA_WRAPPED_CALL, |
94 | PRAGMA_WRITABLE_STRINGS, |
95 | PRAGMA_ZPSYM, |
96 | PRAGMA_COUNT |
97 | } pragma_t; |
98 | |
99 | /* Pragma table */ |
100 | static const struct Pragma { |
101 | const char* Key; /* Keyword */ |
102 | pragma_t Tok; /* Token */ |
103 | } Pragmas[PRAGMA_COUNT] = { |
104 | { "align" , PRAGMA_ALIGN }, |
105 | { "allow-eager-inline" , PRAGMA_ALLOW_EAGER_INLINE }, |
106 | { "bss-name" , PRAGMA_BSS_NAME }, |
107 | { "bssseg" , PRAGMA_BSSSEG }, /* obsolete */ |
108 | { "charmap" , PRAGMA_CHARMAP }, |
109 | { "check-stack" , PRAGMA_CHECK_STACK }, |
110 | { "checkstack" , PRAGMA_CHECKSTACK }, /* obsolete */ |
111 | { "code-name" , PRAGMA_CODE_NAME }, |
112 | { "codeseg" , PRAGMA_CODESEG }, /* obsolete */ |
113 | { "codesize" , PRAGMA_CODESIZE }, |
114 | { "data-name" , PRAGMA_DATA_NAME }, |
115 | { "dataseg" , PRAGMA_DATASEG }, /* obsolete */ |
116 | { "inline-stdfuncs" , PRAGMA_INLINE_STDFUNCS }, |
117 | { "local-strings" , PRAGMA_LOCAL_STRINGS }, |
118 | { "message" , PRAGMA_MESSAGE }, |
119 | { "optimize" , PRAGMA_OPTIMIZE }, |
120 | { "register-vars" , PRAGMA_REGISTER_VARS }, |
121 | { "regvaraddr" , PRAGMA_REGVARADDR }, |
122 | { "regvars" , PRAGMA_REGVARS }, /* obsolete */ |
123 | { "rodata-name" , PRAGMA_RODATA_NAME }, |
124 | { "rodataseg" , PRAGMA_RODATASEG }, /* obsolete */ |
125 | { "signed-chars" , PRAGMA_SIGNED_CHARS }, |
126 | { "signedchars" , PRAGMA_SIGNEDCHARS }, /* obsolete */ |
127 | { "static-locals" , PRAGMA_STATIC_LOCALS }, |
128 | { "staticlocals" , PRAGMA_STATICLOCALS }, /* obsolete */ |
129 | { "warn" , PRAGMA_WARN }, |
130 | { "wrapped-call" , PRAGMA_WRAPPED_CALL }, |
131 | { "writable-strings" , PRAGMA_WRITABLE_STRINGS }, |
132 | { "zpsym" , PRAGMA_ZPSYM }, |
133 | }; |
134 | |
135 | /* Result of ParsePushPop */ |
136 | typedef enum { |
137 | PP_NONE, |
138 | PP_POP, |
139 | PP_PUSH, |
140 | PP_ERROR, |
141 | } PushPopResult; |
142 | |
143 | |
144 | |
145 | /*****************************************************************************/ |
146 | /* Helper functions */ |
147 | /*****************************************************************************/ |
148 | |
149 | |
150 | |
151 | static void PragmaErrorSkip (void) |
152 | /* Called in case of an error, skips tokens until the closing paren or a |
153 | ** semicolon is reached. |
154 | */ |
155 | { |
156 | static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI }; |
157 | SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); |
158 | } |
159 | |
160 | |
161 | |
162 | static int CmpKey (const void* Key, const void* Elem) |
163 | /* Compare function for bsearch */ |
164 | { |
165 | return strcmp ((const char*) Key, ((const struct Pragma*) Elem)->Key); |
166 | } |
167 | |
168 | |
169 | |
170 | static pragma_t FindPragma (const StrBuf* Key) |
171 | /* Find a pragma and return the token. Return PRAGMA_ILLEGAL if the keyword is |
172 | ** not a valid pragma. |
173 | */ |
174 | { |
175 | struct Pragma* P; |
176 | P = bsearch (SB_GetConstBuf (Key), Pragmas, PRAGMA_COUNT, sizeof (Pragmas[0]), CmpKey); |
177 | return P? P->Tok : PRAGMA_ILLEGAL; |
178 | } |
179 | |
180 | |
181 | |
182 | static int GetComma (StrBuf* B) |
183 | /* Expects and skips a comma in B. Prints an error and returns zero if no |
184 | ** comma is found. Return a value <> 0 otherwise. |
185 | */ |
186 | { |
187 | SB_SkipWhite (B); |
188 | if (SB_Get (B) != ',') { |
189 | Error ("Comma expected" ); |
190 | return 0; |
191 | } |
192 | SB_SkipWhite (B); |
193 | return 1; |
194 | } |
195 | |
196 | |
197 | |
198 | static int GetString (StrBuf* B, StrBuf* S) |
199 | /* Expects and skips a string in B. Prints an error and returns zero if no |
200 | ** string is found. Returns a value <> 0 otherwise. |
201 | */ |
202 | { |
203 | if (!SB_GetString (B, S)) { |
204 | Error ("String literal expected" ); |
205 | return 0; |
206 | } |
207 | return 1; |
208 | } |
209 | |
210 | |
211 | |
212 | static int GetNumber (StrBuf* B, long* Val) |
213 | /* Expects and skips a number in B. Prints an eror and returns zero if no |
214 | ** number is found. Returns a value <> 0 otherwise. |
215 | */ |
216 | { |
217 | if (!SB_GetNumber (B, Val)) { |
218 | Error ("Constant integer expected" ); |
219 | return 0; |
220 | } |
221 | return 1; |
222 | } |
223 | |
224 | |
225 | |
226 | static IntStack* GetWarning (StrBuf* B) |
227 | /* Get a warning name from the string buffer. Returns a pointer to the intstack |
228 | ** that holds the state of the warning, and NULL in case of errors. The |
229 | ** function will output error messages in case of problems. |
230 | */ |
231 | { |
232 | IntStack* S = 0; |
233 | StrBuf W = AUTO_STRBUF_INITIALIZER; |
234 | |
235 | /* The warning name is a symbol but the '-' char is allowed within */ |
236 | if (SB_GetSym (B, &W, "-" )) { |
237 | |
238 | /* Map the warning name to an IntStack that contains its state */ |
239 | S = FindWarning (SB_GetConstBuf (&W)); |
240 | |
241 | /* Handle errors */ |
242 | if (S == 0) { |
243 | Error ("Pragma expects a warning name as first argument" ); |
244 | } |
245 | } |
246 | |
247 | /* Deallocate the string */ |
248 | SB_Done (&W); |
249 | |
250 | /* Done */ |
251 | return S; |
252 | } |
253 | |
254 | |
255 | |
256 | static int HasStr (StrBuf* B, const char* E) |
257 | /* Checks if E follows in B. If so, skips it and returns true */ |
258 | { |
259 | unsigned Len = strlen (E); |
260 | if (SB_GetLen (B) - SB_GetIndex (B) >= Len) { |
261 | if (strncmp (SB_GetConstBuf (B) + SB_GetIndex (B), E, Len) == 0) { |
262 | /* Found */ |
263 | SB_SkipMultiple (B, Len); |
264 | return 1; |
265 | } |
266 | } |
267 | return 0; |
268 | } |
269 | |
270 | |
271 | |
272 | static PushPopResult ParsePushPop (StrBuf* B) |
273 | /* Check for and parse the "push" and "pop" keywords. In case of "push", a |
274 | ** following comma is expected and skipped. |
275 | */ |
276 | { |
277 | StrBuf Ident = AUTO_STRBUF_INITIALIZER; |
278 | PushPopResult Res = PP_NONE; |
279 | |
280 | /* Remember the current string index, so we can go back in case of errors */ |
281 | unsigned Index = SB_GetIndex (B); |
282 | |
283 | /* Try to read an identifier */ |
284 | if (SB_GetSym (B, &Ident, 0)) { |
285 | |
286 | /* Check if we have a first argument named "pop" */ |
287 | if (SB_CompareStr (&Ident, "pop" ) == 0) { |
288 | |
289 | Res = PP_POP; |
290 | |
291 | /* Check if we have a first argument named "push" */ |
292 | } else if (SB_CompareStr (&Ident, "push" ) == 0) { |
293 | |
294 | Res = PP_PUSH; |
295 | |
296 | /* Skip the following comma */ |
297 | if (!GetComma (B)) { |
298 | /* Error already flagged by GetComma */ |
299 | Res = PP_ERROR; |
300 | } |
301 | |
302 | } else { |
303 | |
304 | /* Unknown keyword, roll back */ |
305 | SB_SetIndex (B, Index); |
306 | } |
307 | } |
308 | |
309 | /* Free the string buffer and return the result */ |
310 | SB_Done (&Ident); |
311 | return Res; |
312 | } |
313 | |
314 | |
315 | |
316 | static void PopInt (IntStack* S) |
317 | /* Pops an integer from an IntStack. Prints an error if the stack is empty */ |
318 | { |
319 | if (IS_GetCount (S) < 2) { |
320 | Error ("Cannot pop, stack is empty" ); |
321 | } else { |
322 | IS_Drop (S); |
323 | } |
324 | } |
325 | |
326 | |
327 | |
328 | static void PushInt (IntStack* S, long Val) |
329 | /* Pushes an integer onto an IntStack. Prints an error if the stack is full */ |
330 | { |
331 | if (IS_IsFull (S)) { |
332 | Error ("Cannot push: stack overflow" ); |
333 | } else { |
334 | IS_Push (S, Val); |
335 | } |
336 | } |
337 | |
338 | |
339 | |
340 | static int BoolKeyword (StrBuf* Ident) |
341 | /* Check if the identifier in Ident is a keyword for a boolean value. Currently |
342 | ** accepted are true/false/on/off. |
343 | */ |
344 | { |
345 | if (SB_CompareStr (Ident, "true" ) == 0) { |
346 | return 1; |
347 | } |
348 | if (SB_CompareStr (Ident, "on" ) == 0) { |
349 | return 1; |
350 | } |
351 | if (SB_CompareStr (Ident, "false" ) == 0) { |
352 | return 0; |
353 | } |
354 | if (SB_CompareStr (Ident, "off" ) == 0) { |
355 | return 0; |
356 | } |
357 | |
358 | /* Error */ |
359 | Error ("Pragma argument must be one of 'on', 'off', 'true' or 'false'" ); |
360 | return 0; |
361 | } |
362 | |
363 | |
364 | |
365 | /*****************************************************************************/ |
366 | /* Pragma handling functions */ |
367 | /*****************************************************************************/ |
368 | |
369 | |
370 | |
371 | static void StringPragma (StrBuf* B, void (*Func) (const char*)) |
372 | /* Handle a pragma that expects a string parameter */ |
373 | { |
374 | StrBuf S = AUTO_STRBUF_INITIALIZER; |
375 | |
376 | /* We expect a string here */ |
377 | if (GetString (B, &S)) { |
378 | /* Call the given function with the string argument */ |
379 | Func (SB_GetConstBuf (&S)); |
380 | } |
381 | |
382 | /* Call the string buf destructor */ |
383 | SB_Done (&S); |
384 | } |
385 | |
386 | |
387 | |
388 | static void SegNamePragma (StrBuf* B, segment_t Seg) |
389 | /* Handle a pragma that expects a segment name parameter */ |
390 | { |
391 | const char* Name; |
392 | StrBuf S = AUTO_STRBUF_INITIALIZER; |
393 | int Push = 0; |
394 | |
395 | /* Check for the "push" or "pop" keywords */ |
396 | switch (ParsePushPop (B)) { |
397 | |
398 | case PP_NONE: |
399 | break; |
400 | |
401 | case PP_PUSH: |
402 | Push = 1; |
403 | break; |
404 | |
405 | case PP_POP: |
406 | /* Pop the old value and output it */ |
407 | PopSegName (Seg); |
408 | |
409 | /* BSS variables are output at the end of the compilation. Don't |
410 | ** bother to change their segment, now. |
411 | */ |
412 | if (Seg != SEG_BSS) { |
413 | g_segname (Seg); |
414 | } |
415 | |
416 | /* Done */ |
417 | goto ExitPoint; |
418 | |
419 | case PP_ERROR: |
420 | /* Bail out */ |
421 | goto ExitPoint; |
422 | |
423 | default: |
424 | Internal ("Invalid result from ParsePushPop" ); |
425 | |
426 | } |
427 | |
428 | /* A string argument must follow */ |
429 | if (!GetString (B, &S)) { |
430 | goto ExitPoint; |
431 | } |
432 | |
433 | /* Get the string */ |
434 | Name = SB_GetConstBuf (&S); |
435 | |
436 | /* Check if the name is valid */ |
437 | if (ValidSegName (Name)) { |
438 | |
439 | /* Set the new name */ |
440 | if (Push) { |
441 | PushSegName (Seg, Name); |
442 | } else { |
443 | SetSegName (Seg, Name); |
444 | } |
445 | |
446 | /* BSS variables are output at the end of the compilation. Don't |
447 | ** bother to change their segment, now. |
448 | */ |
449 | if (Seg != SEG_BSS) { |
450 | g_segname (Seg); |
451 | } |
452 | |
453 | } else { |
454 | |
455 | /* Segment name is invalid */ |
456 | Error ("Illegal segment name: '%s'" , Name); |
457 | |
458 | } |
459 | |
460 | ExitPoint: |
461 | /* Call the string buf destructor */ |
462 | SB_Done (&S); |
463 | } |
464 | |
465 | |
466 | static void WrappedCallPragma (StrBuf* B) |
467 | /* Handle the wrapped-call pragma */ |
468 | { |
469 | StrBuf S = AUTO_STRBUF_INITIALIZER; |
470 | const char *Name; |
471 | long Val; |
472 | SymEntry *Entry; |
473 | |
474 | /* Check for the "push" or "pop" keywords */ |
475 | switch (ParsePushPop (B)) { |
476 | |
477 | case PP_NONE: |
478 | Error ("Push or pop required" ); |
479 | break; |
480 | |
481 | case PP_PUSH: |
482 | break; |
483 | |
484 | case PP_POP: |
485 | PopWrappedCall(); |
486 | |
487 | /* Done */ |
488 | goto ExitPoint; |
489 | |
490 | case PP_ERROR: |
491 | /* Bail out */ |
492 | goto ExitPoint; |
493 | |
494 | default: |
495 | Internal ("Invalid result from ParsePushPop" ); |
496 | |
497 | } |
498 | |
499 | /* A symbol argument must follow */ |
500 | if (!SB_GetSym (B, &S, NULL)) { |
501 | goto ExitPoint; |
502 | } |
503 | |
504 | /* Skip the following comma */ |
505 | if (!GetComma (B)) { |
506 | /* Error already flagged by GetComma */ |
507 | Error ("Value required for wrapped-call identifier" ); |
508 | goto ExitPoint; |
509 | } |
510 | |
511 | if (!GetNumber (B, &Val)) { |
512 | Error ("Value required for wrapped-call identifier" ); |
513 | goto ExitPoint; |
514 | } |
515 | |
516 | if (Val < 0 || Val > 255) { |
517 | Error ("Identifier must be between 0-255" ); |
518 | goto ExitPoint; |
519 | } |
520 | |
521 | /* Get the string */ |
522 | Name = SB_GetConstBuf (&S); |
523 | Entry = FindSym(Name); |
524 | |
525 | /* Check if the name is valid */ |
526 | if (Entry && Entry->Flags & SC_FUNC) { |
527 | |
528 | PushWrappedCall(Entry, (unsigned char) Val); |
529 | Entry->Flags |= SC_REF; |
530 | Entry->V.F.Func->Flags |= FD_CALL_WRAPPER; |
531 | |
532 | } else { |
533 | |
534 | /* Segment name is invalid */ |
535 | Error ("Wrapped-call target does not exist or is not a function" ); |
536 | |
537 | } |
538 | |
539 | ExitPoint: |
540 | /* Call the string buf destructor */ |
541 | SB_Done (&S); |
542 | } |
543 | |
544 | |
545 | |
546 | static void CharMapPragma (StrBuf* B) |
547 | /* Change the character map */ |
548 | { |
549 | long Index, C; |
550 | |
551 | /* Read the character index */ |
552 | if (!GetNumber (B, &Index)) { |
553 | return; |
554 | } |
555 | if (Index < 0 || Index > 255) { |
556 | Error ("Character index out of range" ); |
557 | return; |
558 | } |
559 | |
560 | /* Comma follows */ |
561 | if (!GetComma (B)) { |
562 | return; |
563 | } |
564 | |
565 | /* Read the character code */ |
566 | if (!GetNumber (B, &C)) { |
567 | return; |
568 | } |
569 | if (C < 0 || C > 255) { |
570 | Error ("Character code out of range" ); |
571 | return; |
572 | } |
573 | |
574 | /* Warn about remapping character code 0x00 |
575 | ** (except when remapping it back to itself). |
576 | */ |
577 | if (Index + C != 0 && IS_Get (&WarnRemapZero)) { |
578 | if (Index == 0) { |
579 | Warning ("Remapping from 0 is dangerous with string functions" ); |
580 | } |
581 | else if (C == 0) { |
582 | Warning ("Remapping to 0 can make string functions stop unexpectedly" ); |
583 | } |
584 | } |
585 | |
586 | /* Remap the character */ |
587 | TgtTranslateSet ((unsigned) Index, (unsigned char) C); |
588 | } |
589 | |
590 | |
591 | |
592 | static void WarnPragma (StrBuf* B) |
593 | /* Enable/disable warnings */ |
594 | { |
595 | long Val; |
596 | int Push; |
597 | |
598 | /* A warning name must follow */ |
599 | IntStack* S = GetWarning (B); |
600 | if (S == 0) { |
601 | return; |
602 | } |
603 | |
604 | /* Comma follows */ |
605 | if (!GetComma (B)) { |
606 | return; |
607 | } |
608 | |
609 | /* Check for the "push" or "pop" keywords */ |
610 | switch (ParsePushPop (B)) { |
611 | |
612 | case PP_NONE: |
613 | Push = 0; |
614 | break; |
615 | |
616 | case PP_PUSH: |
617 | Push = 1; |
618 | break; |
619 | |
620 | case PP_POP: |
621 | /* Pop the old value and bail out */ |
622 | PopInt (S); |
623 | return; |
624 | |
625 | case PP_ERROR: |
626 | /* Bail out */ |
627 | return; |
628 | |
629 | default: |
630 | Internal ("Invalid result from ParsePushPop" ); |
631 | } |
632 | |
633 | /* Boolean argument follows */ |
634 | if (HasStr (B, "true" ) || HasStr (B, "on" )) { |
635 | Val = 1; |
636 | } else if (HasStr (B, "false" ) || HasStr (B, "off" )) { |
637 | Val = 0; |
638 | } else if (!SB_GetNumber (B, &Val)) { |
639 | Error ("Invalid pragma argument" ); |
640 | return; |
641 | } |
642 | |
643 | /* Set/push the new value */ |
644 | if (Push) { |
645 | PushInt (S, Val); |
646 | } else { |
647 | IS_Set (S, Val); |
648 | } |
649 | } |
650 | |
651 | |
652 | |
653 | static void FlagPragma (StrBuf* B, IntStack* Stack) |
654 | /* Handle a pragma that expects a boolean paramater */ |
655 | { |
656 | StrBuf Ident = AUTO_STRBUF_INITIALIZER; |
657 | long Val; |
658 | int Push; |
659 | |
660 | |
661 | /* Try to read an identifier */ |
662 | int IsIdent = SB_GetSym (B, &Ident, 0); |
663 | |
664 | /* Check if we have a first argument named "pop" */ |
665 | if (IsIdent && SB_CompareStr (&Ident, "pop" ) == 0) { |
666 | PopInt (Stack); |
667 | /* No other arguments allowed */ |
668 | return; |
669 | } |
670 | |
671 | /* Check if we have a first argument named "push" */ |
672 | if (IsIdent && SB_CompareStr (&Ident, "push" ) == 0) { |
673 | Push = 1; |
674 | if (!GetComma (B)) { |
675 | goto ExitPoint; |
676 | } |
677 | IsIdent = SB_GetSym (B, &Ident, 0); |
678 | } else { |
679 | Push = 0; |
680 | } |
681 | |
682 | /* Boolean argument follows */ |
683 | if (IsIdent) { |
684 | Val = BoolKeyword (&Ident); |
685 | } else if (!GetNumber (B, &Val)) { |
686 | goto ExitPoint; |
687 | } |
688 | |
689 | /* Set/push the new value */ |
690 | if (Push) { |
691 | PushInt (Stack, Val); |
692 | } else { |
693 | IS_Set (Stack, Val); |
694 | } |
695 | |
696 | ExitPoint: |
697 | /* Free the identifier */ |
698 | SB_Done (&Ident); |
699 | } |
700 | |
701 | |
702 | |
703 | static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High) |
704 | /* Handle a pragma that expects an int paramater */ |
705 | { |
706 | long Val; |
707 | int Push; |
708 | |
709 | /* Check for the "push" or "pop" keywords */ |
710 | switch (ParsePushPop (B)) { |
711 | |
712 | case PP_NONE: |
713 | Push = 0; |
714 | break; |
715 | |
716 | case PP_PUSH: |
717 | Push = 1; |
718 | break; |
719 | |
720 | case PP_POP: |
721 | /* Pop the old value and bail out */ |
722 | PopInt (Stack); |
723 | return; |
724 | |
725 | case PP_ERROR: |
726 | /* Bail out */ |
727 | return; |
728 | |
729 | default: |
730 | Internal ("Invalid result from ParsePushPop" ); |
731 | |
732 | } |
733 | |
734 | /* Integer argument follows */ |
735 | if (!GetNumber (B, &Val)) { |
736 | return; |
737 | } |
738 | |
739 | /* Check the argument */ |
740 | if (Val < Low || Val > High) { |
741 | Error ("Pragma argument out of bounds (%ld-%ld)" , Low, High); |
742 | return; |
743 | } |
744 | |
745 | /* Set/push the new value */ |
746 | if (Push) { |
747 | PushInt (Stack, Val); |
748 | } else { |
749 | IS_Set (Stack, Val); |
750 | } |
751 | } |
752 | |
753 | |
754 | |
755 | static void MakeMessage (const char* Message) |
756 | { |
757 | fprintf (stderr, "%s(%u): Note: %s\n" , GetInputName (CurTok.LI), GetInputLine (CurTok.LI), Message); |
758 | } |
759 | |
760 | |
761 | |
762 | static void ParsePragma (void) |
763 | /* Parse the contents of the _Pragma statement */ |
764 | { |
765 | pragma_t Pragma; |
766 | StrBuf Ident = AUTO_STRBUF_INITIALIZER; |
767 | |
768 | /* Create a string buffer from the string literal */ |
769 | StrBuf B = AUTO_STRBUF_INITIALIZER; |
770 | SB_Append (&B, GetLiteralStrBuf (CurTok.SVal)); |
771 | |
772 | /* Skip the string token */ |
773 | NextToken (); |
774 | |
775 | /* Get the pragma name from the string */ |
776 | SB_SkipWhite (&B); |
777 | if (!SB_GetSym (&B, &Ident, "-" )) { |
778 | Error ("Invalid pragma" ); |
779 | goto ExitPoint; |
780 | } |
781 | |
782 | /* Search for the name */ |
783 | Pragma = FindPragma (&Ident); |
784 | |
785 | /* Do we know this pragma? */ |
786 | if (Pragma == PRAGMA_ILLEGAL) { |
787 | /* According to the ANSI standard, we're not allowed to generate errors |
788 | ** for unknown pragmas, but warn about them if enabled (the default). |
789 | */ |
790 | if (IS_Get (&WarnUnknownPragma)) { |
791 | Warning ("Unknown pragma '%s'" , SB_GetConstBuf (&Ident)); |
792 | } |
793 | goto ExitPoint; |
794 | } |
795 | |
796 | /* Check for an open paren */ |
797 | SB_SkipWhite (&B); |
798 | if (SB_Get (&B) != '(') { |
799 | Error ("'(' expected" ); |
800 | goto ExitPoint; |
801 | } |
802 | |
803 | /* Skip white space before the argument */ |
804 | SB_SkipWhite (&B); |
805 | |
806 | /* Switch for the different pragmas */ |
807 | switch (Pragma) { |
808 | |
809 | case PRAGMA_ALIGN: |
810 | IntPragma (&B, &DataAlignment, 1, 4096); |
811 | break; |
812 | |
813 | case PRAGMA_ALLOW_EAGER_INLINE: |
814 | FlagPragma (&B, &EagerlyInlineFuncs); |
815 | break; |
816 | |
817 | case PRAGMA_BSSSEG: |
818 | Warning ("#pragma bssseg is obsolete, please use #pragma bss-name instead" ); |
819 | /* FALLTHROUGH */ |
820 | case PRAGMA_BSS_NAME: |
821 | SegNamePragma (&B, SEG_BSS); |
822 | break; |
823 | |
824 | case PRAGMA_CHARMAP: |
825 | CharMapPragma (&B); |
826 | break; |
827 | |
828 | case PRAGMA_CHECKSTACK: |
829 | Warning ("#pragma checkstack is obsolete, please use #pragma check-stack instead" ); |
830 | /* FALLTHROUGH */ |
831 | case PRAGMA_CHECK_STACK: |
832 | FlagPragma (&B, &CheckStack); |
833 | break; |
834 | |
835 | case PRAGMA_CODESEG: |
836 | Warning ("#pragma codeseg is obsolete, please use #pragma code-name instead" ); |
837 | /* FALLTHROUGH */ |
838 | case PRAGMA_CODE_NAME: |
839 | SegNamePragma (&B, SEG_CODE); |
840 | break; |
841 | |
842 | case PRAGMA_CODESIZE: |
843 | IntPragma (&B, &CodeSizeFactor, 10, 1000); |
844 | break; |
845 | |
846 | case PRAGMA_DATASEG: |
847 | Warning ("#pragma dataseg is obsolete, please use #pragma data-name instead" ); |
848 | /* FALLTHROUGH */ |
849 | case PRAGMA_DATA_NAME: |
850 | SegNamePragma (&B, SEG_DATA); |
851 | break; |
852 | |
853 | case PRAGMA_INLINE_STDFUNCS: |
854 | FlagPragma (&B, &InlineStdFuncs); |
855 | break; |
856 | |
857 | case PRAGMA_LOCAL_STRINGS: |
858 | FlagPragma (&B, &LocalStrings); |
859 | break; |
860 | |
861 | case PRAGMA_MESSAGE: |
862 | StringPragma (&B, MakeMessage); |
863 | break; |
864 | |
865 | case PRAGMA_OPTIMIZE: |
866 | FlagPragma (&B, &Optimize); |
867 | break; |
868 | |
869 | case PRAGMA_REGVARADDR: |
870 | FlagPragma (&B, &AllowRegVarAddr); |
871 | break; |
872 | |
873 | case PRAGMA_REGVARS: |
874 | Warning ("#pragma regvars is obsolete, please use #pragma register-vars instead" ); |
875 | /* FALLTHROUGH */ |
876 | case PRAGMA_REGISTER_VARS: |
877 | FlagPragma (&B, &EnableRegVars); |
878 | break; |
879 | |
880 | case PRAGMA_RODATASEG: |
881 | Warning ("#pragma rodataseg is obsolete, please use #pragma rodata-name instead" ); |
882 | /* FALLTHROUGH */ |
883 | case PRAGMA_RODATA_NAME: |
884 | SegNamePragma (&B, SEG_RODATA); |
885 | break; |
886 | |
887 | case PRAGMA_SIGNEDCHARS: |
888 | Warning ("#pragma signedchars is obsolete, please use #pragma signed-chars instead" ); |
889 | /* FALLTHROUGH */ |
890 | case PRAGMA_SIGNED_CHARS: |
891 | FlagPragma (&B, &SignedChars); |
892 | break; |
893 | |
894 | case PRAGMA_STATICLOCALS: |
895 | Warning ("#pragma staticlocals is obsolete, please use #pragma static-locals instead" ); |
896 | /* FALLTHROUGH */ |
897 | case PRAGMA_STATIC_LOCALS: |
898 | FlagPragma (&B, &StaticLocals); |
899 | break; |
900 | |
901 | case PRAGMA_WRAPPED_CALL: |
902 | WrappedCallPragma(&B); |
903 | break; |
904 | |
905 | case PRAGMA_WARN: |
906 | WarnPragma (&B); |
907 | break; |
908 | |
909 | case PRAGMA_WRITABLE_STRINGS: |
910 | FlagPragma (&B, &WritableStrings); |
911 | break; |
912 | |
913 | case PRAGMA_ZPSYM: |
914 | StringPragma (&B, MakeZPSym); |
915 | break; |
916 | |
917 | default: |
918 | Internal ("Invalid pragma" ); |
919 | } |
920 | |
921 | /* Closing paren expected */ |
922 | SB_SkipWhite (&B); |
923 | if (SB_Get (&B) != ')') { |
924 | Error ("')' expected" ); |
925 | goto ExitPoint; |
926 | } |
927 | SB_SkipWhite (&B); |
928 | |
929 | /* Allow an optional semicolon to be compatible with the old syntax */ |
930 | if (SB_Peek (&B) == ';') { |
931 | SB_Skip (&B); |
932 | SB_SkipWhite (&B); |
933 | } |
934 | |
935 | /* Make sure nothing follows */ |
936 | if (SB_Peek (&B) != '\0') { |
937 | Error ("Unexpected input following pragma directive" ); |
938 | } |
939 | |
940 | ExitPoint: |
941 | /* Release the string buffers */ |
942 | SB_Done (&B); |
943 | SB_Done (&Ident); |
944 | } |
945 | |
946 | |
947 | |
948 | void DoPragma (void) |
949 | /* Handle pragmas. These come always in form of the new C99 _Pragma() operator. */ |
950 | { |
951 | /* Skip the token itself */ |
952 | NextToken (); |
953 | |
954 | /* We expect an opening paren */ |
955 | if (!ConsumeLParen ()) { |
956 | return; |
957 | } |
958 | |
959 | /* String literal */ |
960 | if (CurTok.Tok != TOK_SCONST) { |
961 | |
962 | /* Print a diagnostic */ |
963 | Error ("String literal expected" ); |
964 | |
965 | /* Try some smart error recovery: Skip tokens until we reach the |
966 | ** enclosing paren, or a semicolon. |
967 | */ |
968 | PragmaErrorSkip (); |
969 | |
970 | } else { |
971 | |
972 | /* Parse the _Pragma statement */ |
973 | ParsePragma (); |
974 | } |
975 | |
976 | /* Closing paren needed */ |
977 | ConsumeRParen (); |
978 | } |
979 | |