]> git.sur5r.net Git - cc65/blob - src/cc65/scanner.c
Redoing the pragma stuff
[cc65] / src / cc65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*                      Source file line info structure                      */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2002 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
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 <stdlib.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <ctype.h>
41
42 /* common */
43 #include "chartype.h"
44 #include "tgttrans.h"
45
46 /* cc65 */
47 #include "datatype.h"
48 #include "error.h"
49 #include "function.h"
50 #include "global.h"
51 #include "hexval.h"
52 #include "ident.h"
53 #include "input.h"
54 #include "litpool.h"
55 #include "preproc.h"
56 #include "symtab.h"
57 #include "util.h"
58 #include "scanner.h"
59
60
61
62 /*****************************************************************************/
63 /*                                   data                                    */
64 /*****************************************************************************/
65
66
67
68 Token CurTok;           /* The current token */
69 Token NextTok;          /* The next token */
70
71
72
73 /* Token types */
74 #define TT_C    0               /* ANSI C token */
75 #define TT_EXT  1               /* cc65 extension */
76
77 /* Token table */
78 static const struct Keyword {
79     char*           Key;        /* Keyword name */
80     unsigned char   Tok;        /* The token */
81     unsigned char   Type;       /* Token type */
82 } Keywords [] = {
83     { "_Pragma",        TOK_PRAGMA,     TT_C    },
84     { "__A__",          TOK_A,          TT_C    },
85     { "__AX__",         TOK_AX,         TT_C    },
86     { "__EAX__",        TOK_EAX,        TT_C    },
87     { "__X__",          TOK_X,          TT_C    },
88     { "__Y__",          TOK_Y,          TT_C    },
89     { "__asm__",        TOK_ASM,        TT_C    },
90     { "__attribute__",  TOK_ATTRIBUTE,  TT_C    },
91     { "__far__",        TOK_FAR,        TT_C    },
92     { "__fastcall__",   TOK_FASTCALL,   TT_C    },
93     { "asm",            TOK_ASM,        TT_EXT  },
94     { "auto",           TOK_AUTO,       TT_C    },
95     { "break",          TOK_BREAK,      TT_C    },
96     { "case",           TOK_CASE,       TT_C    },
97     { "char",           TOK_CHAR,       TT_C    },
98     { "const",          TOK_CONST,      TT_C    },
99     { "continue",       TOK_CONTINUE,   TT_C    },
100     { "default",        TOK_DEFAULT,    TT_C    },
101     { "do",             TOK_DO,         TT_C    },
102     { "double",         TOK_DOUBLE,     TT_C    },
103     { "else",           TOK_ELSE,       TT_C    },
104     { "enum",           TOK_ENUM,       TT_C    },
105     { "extern",         TOK_EXTERN,     TT_C    },
106     { "far",            TOK_FAR,        TT_EXT  },
107     { "fastcall",       TOK_FASTCALL,   TT_EXT  },
108     { "float",          TOK_FLOAT,      TT_C    },
109     { "for",            TOK_FOR,        TT_C    },
110     { "goto",           TOK_GOTO,       TT_C    },
111     { "if",             TOK_IF,         TT_C    },
112     { "int",            TOK_INT,        TT_C    },
113     { "long",           TOK_LONG,       TT_C    },
114     { "register",       TOK_REGISTER,   TT_C    },
115     { "return",         TOK_RETURN,     TT_C    },
116     { "short",          TOK_SHORT,      TT_C    },
117     { "signed",         TOK_SIGNED,     TT_C    },
118     { "sizeof",         TOK_SIZEOF,     TT_C    },
119     { "static",         TOK_STATIC,     TT_C    },
120     { "struct",         TOK_STRUCT,     TT_C    },
121     { "switch",         TOK_SWITCH,     TT_C    },
122     { "typedef",        TOK_TYPEDEF,    TT_C    },
123     { "union",          TOK_UNION,      TT_C    },
124     { "unsigned",       TOK_UNSIGNED,   TT_C    },
125     { "void",           TOK_VOID,       TT_C    },
126     { "volatile",       TOK_VOLATILE,   TT_C    },
127     { "while",          TOK_WHILE,      TT_C    },
128 };
129 #define KEY_COUNT       (sizeof (Keywords) / sizeof (Keywords [0]))
130
131
132
133 /* Stuff for determining the type of an integer constant */
134 #define IT_INT          0x01
135 #define IT_UINT         0x02
136 #define IT_LONG         0x04
137 #define IT_ULONG        0x08
138
139
140
141 /*****************************************************************************/
142 /*                                   code                                    */
143 /*****************************************************************************/
144
145
146
147 static int CmpKey (const void* Key, const void* Elem)
148 /* Compare function for bsearch */
149 {
150     return strcmp ((const char*) Key, ((const struct Keyword*) Elem)->Key);
151 }
152
153
154
155 static int FindKey (const char* Key)
156 /* Find a keyword and return the token. Return IDENT if the token is not a
157  * keyword.
158  */
159 {
160     struct Keyword* K;
161     K = bsearch (Key, Keywords, KEY_COUNT, sizeof (Keywords [0]), CmpKey);
162     if (K && (K->Type != TT_EXT || ANSI == 0)) {
163         return K->Tok;
164     } else {
165         return TOK_IDENT;
166     }
167 }
168
169
170
171 static int SkipWhite (void)
172 /* Skip white space in the input stream, reading and preprocessing new lines
173  * if necessary. Return 0 if end of file is reached, return 1 otherwise.
174  */
175 {
176     while (1) {
177         while (CurC == 0) {
178             if (NextLine () == 0) {
179                 return 0;
180             }
181             Preprocess ();
182         }
183         if (IsSpace (CurC)) {
184             NextChar ();
185         } else {
186             return 1;
187         }
188     }
189 }
190
191
192
193 void SymName (char* s)
194 /* Get symbol from input stream */
195 {
196     unsigned k = 0;
197     do {
198         if (k != MAX_IDENTLEN) {
199             ++k;
200             *s++ = CurC;
201         }
202         NextChar ();
203     } while (IsIdent (CurC) || IsDigit (CurC));
204     *s = '\0';
205 }
206
207
208
209 int IsSym (char *s)
210 /* Get symbol from input stream or return 0 if not a symbol. */
211 {
212     if (IsIdent (CurC)) {
213         SymName (s);
214         return 1;
215     } else {
216         return 0;
217     }
218 }
219
220
221
222 static void UnknownChar (char C)
223 /* Error message for unknown character */
224 {
225     Error ("Invalid input character with code %02X", C & 0xFF);
226     NextChar ();                        /* Skip */
227 }
228
229
230
231 static void SetTok (int tok)
232 /* Set NextTok.Tok and bump line ptr */
233 {
234     NextTok.Tok = tok;
235     NextChar ();
236 }
237
238
239
240 static int SignExtendChar (int C)
241 /* Do correct sign extension of a character */
242 {
243     if (SignedChars && (C & 0x80) != 0) {
244         return C | ~0xFF;
245     } else {
246         return C & 0xFF;
247     }
248 }
249
250
251
252 static int ParseChar (void)
253 /* Parse a character. Converts \n into EOL, etc. */
254 {
255     int i;
256     unsigned val;
257     int C;
258
259     /* Check for escape chars */
260     if (CurC == '\\') {
261         NextChar ();
262         switch (CurC) {
263             case 'b':
264                 C = '\b';
265                 break;
266             case 'f':
267                 C = '\f';
268                 break;
269             case 'r':
270                 C = '\r';
271                 break;
272             case 'n':
273                 C = '\n';
274                 break;
275             case 't':
276                 C = '\t';
277                 break;
278             case '\"':
279                 C = '\"';
280                 break;
281             case '\'':
282                 C = '\'';
283                 break;
284             case '\\':
285                 C = '\\';
286                 break;
287             case 'x':
288             case 'X':
289                 /* Hex character constant */
290                 NextChar ();
291                 val = HexVal (CurC) << 4;
292                 NextChar ();
293                 C = val | HexVal (CurC);        /* Do not translate */
294                 break;
295             case '0':
296             case '1':
297                 /* Octal constant */
298                 i = 0;
299                 C = CurC - '0';
300                 while (NextC >= '0' && NextC <= '7' && i++ < 4) {
301                     NextChar ();
302                     C = (C << 3) | (CurC - '0');
303                 }
304                 break;
305             default:
306                 Error ("Illegal character constant");
307                 C = ' ';
308                 break;
309         }
310     } else {
311         C = CurC;
312     }
313
314     /* Skip the character read */
315     NextChar ();
316
317     /* Do correct sign extension */
318     return SignExtendChar (C);
319 }
320
321
322
323 static void CharConst (void)
324 /* Parse a character constant. */
325 {
326     int C;
327
328     /* Skip the quote */
329     NextChar ();
330
331     /* Get character */
332     C = ParseChar ();
333
334     /* Check for closing quote */
335     if (CurC != '\'') {
336         Error ("`\'' expected");
337     } else {
338         /* Skip the quote */
339         NextChar ();
340     }
341
342     /* Setup values and attributes */
343     NextTok.Tok  = TOK_CCONST;
344
345     /* Translate into target charset */
346     NextTok.IVal = SignExtendChar (TgtTranslateChar (C));
347
348     /* Character constants have type int */
349     NextTok.Type = type_int;
350 }
351
352
353
354 static void StringConst (void)
355 /* Parse a quoted string */
356 {
357     NextTok.IVal = GetLiteralPoolOffs ();
358     NextTok.Tok  = TOK_SCONST;
359
360     /* Be sure to concatenate strings */
361     while (CurC == '\"') {
362
363         /* Skip the quote char */
364         NextChar ();
365
366         while (CurC != '\"') {
367             if (CurC == '\0') {
368                 Error ("Unexpected newline");
369                 break;
370             }
371             AddLiteralChar (ParseChar ());
372         }
373
374         /* Skip closing quote char if there was one */
375         NextChar ();
376
377         /* Skip white space, read new input */
378         SkipWhite ();
379
380     }
381
382     /* Terminate the string */
383     AddLiteralChar ('\0');
384 }
385
386
387
388 void NextToken (void)
389 /* Get next token from input stream */
390 {
391     ident token;
392
393     /* We have to skip white space here before shifting tokens, since the
394      * tokens and the current line info is invalid at startup and will get
395      * initialized by reading the first time from the file. Remember if
396      * we were at end of input and handle that later.
397      */
398     int GotEOF = (SkipWhite() == 0);
399
400     /* Current token is the lookahead token */
401     if (CurTok.LI) {
402         ReleaseLineInfo (CurTok.LI);
403     }
404     CurTok = NextTok;
405
406     /* Remember the starting position of the next token */
407     NextTok.LI = UseLineInfo (GetCurLineInfo ());
408
409     /* Now handle end of input. */
410     if (GotEOF) {
411         /* End of file reached */
412         NextTok.Tok = TOK_CEOF;
413         return;
414     }
415
416     /* Determine the next token from the lookahead */
417     if (IsDigit (CurC)) {
418
419         /* A number */
420         int HaveSuffix;         /* True if we have a type suffix */
421         unsigned types;         /* Possible types */
422         unsigned base;
423         unsigned long k;        /* Value */
424
425         k     = 0;
426         base  = 10;
427         types = IT_INT | IT_LONG | IT_ULONG;
428
429         if (CurC == '0') {
430             /* Octal or hex constants may also be of type unsigned int */
431             types = IT_INT | IT_UINT | IT_LONG | IT_ULONG;
432             /* gobble 0 and examin next char */
433             NextChar ();
434             if (toupper (CurC) == 'X') {
435                 base = 16;
436                 NextTok.Type = type_uint;
437                 NextChar ();    /* gobble "x" */
438             } else {
439                 base = 8;
440             }
441         }
442         while (1) {
443             if (IsDigit (CurC)) {
444                 k = k * base + (CurC - '0');
445             } else if (base == 16 && IsXDigit (CurC)) {
446                 k = (k << 4) + HexVal (CurC);
447             } else {
448                 break;          /* not digit */
449             }
450             NextChar ();        /* gobble char */
451         }
452
453         /* Check for a suffix */
454         HaveSuffix = 1;
455         if (CurC == 'u' || CurC == 'U') {
456             /* Unsigned type */
457             NextChar ();
458             if (toupper (CurC) != 'L') {
459                 types = IT_UINT | IT_ULONG;
460             } else {
461                 NextChar ();
462                 types = IT_ULONG;
463             }
464         } else if (CurC == 'l' || CurC == 'L') {
465             /* Long type */
466             NextChar ();
467             if (toupper (CurC) != 'U') {
468                 types = IT_LONG | IT_ULONG;
469             } else {
470                 NextChar ();
471                 types = IT_ULONG;
472             }
473         } else {
474             HaveSuffix = 0;
475         }
476
477         /* Check the range to determine the type */
478         if (k > 0x7FFF) {
479             /* Out of range for int */
480             types &= ~IT_INT;
481             /* If the value is in the range 0x8000..0xFFFF, unsigned int is not
482              * allowed, and we don't have a type specifying suffix, emit a
483              * warning.
484              */
485             if (k <= 0xFFFF && (types & IT_UINT) == 0 && !HaveSuffix) {
486                 Warning ("Constant is long");
487             }
488         }
489         if (k > 0xFFFF) {
490             /* Out of range for unsigned int */
491             types &= ~IT_UINT;
492         }
493         if (k > 0x7FFFFFFF) {
494             /* Out of range for long int */
495             types &= ~IT_LONG;
496         }
497
498         /* Now set the type string to the smallest type in types */
499         if (types & IT_INT) {
500             NextTok.Type = type_int;
501         } else if (types & IT_UINT) {
502             NextTok.Type = type_uint;
503         } else if (types & IT_LONG) {
504             NextTok.Type = type_long;
505         } else {
506             NextTok.Type = type_ulong;
507         }
508
509         /* Set the value and the token */
510         NextTok.IVal = k;
511         NextTok.Tok  = TOK_ICONST;
512         return;
513     }
514
515     if (IsSym (token)) {
516
517         /* Check for a keyword */
518         if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
519             /* Reserved word found */
520             return;
521         }
522         /* No reserved word, check for special symbols */
523         if (token [0] == '_') {
524             /* Special symbols */
525             if (strcmp (token, "__FILE__") == 0) {
526                 NextTok.IVal = AddLiteral (GetCurrentFile());
527                 NextTok.Tok  = TOK_SCONST;
528                 return;
529             } else if (strcmp (token, "__LINE__") == 0) {
530                 NextTok.Tok  = TOK_ICONST;
531                 NextTok.IVal = GetCurrentLine();
532                 NextTok.Type = type_int;
533                 return;
534             } else if (strcmp (token, "__func__") == 0) {
535                 /* __func__ is only defined in functions */
536                 if (CurrentFunc) {
537                     NextTok.IVal = AddLiteral (F_GetFuncName (CurrentFunc));
538                     NextTok.Tok  = TOK_SCONST;
539                     return;
540                 }
541             }
542         }
543
544         /* No reserved word but identifier */
545         strcpy (NextTok.Ident, token);
546         NextTok.Tok = TOK_IDENT;
547         return;
548     }
549
550     /* Monstrous switch statement ahead... */
551     switch (CurC) {
552
553         case '!':
554             NextChar ();
555             if (CurC == '=') {
556                 SetTok (TOK_NE);
557             } else {
558                 NextTok.Tok = TOK_BOOL_NOT;
559             }
560             break;
561
562         case '\"':
563             StringConst ();
564             break;
565
566         case '%':
567             NextChar ();
568             if (CurC == '=') {
569                 SetTok (TOK_MOD_ASSIGN);
570             } else {
571                 NextTok.Tok = TOK_MOD;
572             }
573             break;
574
575         case '&':
576             NextChar ();
577             switch (CurC) {
578                 case '&':
579                     SetTok (TOK_BOOL_AND);
580                     break;
581                 case '=':
582                     SetTok (TOK_AND_ASSIGN);
583                     break;
584                 default:
585                     NextTok.Tok = TOK_AND;
586             }
587             break;
588
589         case '\'':
590             CharConst ();
591             break;
592
593         case '(':
594             SetTok (TOK_LPAREN);
595             break;
596
597         case ')':
598             SetTok (TOK_RPAREN);
599             break;
600
601         case '*':
602             NextChar ();
603             if (CurC == '=') {
604                 SetTok (TOK_MUL_ASSIGN);
605             } else {
606                 NextTok.Tok = TOK_STAR;
607             }
608             break;
609
610         case '+':
611             NextChar ();
612             switch (CurC) {
613                 case '+':
614                     SetTok (TOK_INC);
615                     break;
616                 case '=':
617                     SetTok (TOK_PLUS_ASSIGN);
618                     break;
619                 default:
620                     NextTok.Tok = TOK_PLUS;
621             }
622             break;
623
624         case ',':
625             SetTok (TOK_COMMA);
626             break;
627
628         case '-':
629             NextChar ();
630             switch (CurC) {
631                 case '-':
632                     SetTok (TOK_DEC);
633                     break;
634                 case '=':
635                     SetTok (TOK_MINUS_ASSIGN);
636                     break;
637                 case '>':
638                     SetTok (TOK_PTR_REF);
639                     break;
640                 default:
641                     NextTok.Tok = TOK_MINUS;
642             }
643             break;
644
645         case '.':
646             NextChar ();
647             if (CurC == '.') {
648                 NextChar ();
649                 if (CurC == '.') {
650                     SetTok (TOK_ELLIPSIS);
651                 } else {
652                     UnknownChar (CurC);
653                 }
654             } else {
655                 NextTok.Tok = TOK_DOT;
656             }
657             break;
658
659         case '/':
660             NextChar ();
661             if (CurC == '=') {
662                 SetTok (TOK_DIV_ASSIGN);
663             } else {
664                 NextTok.Tok = TOK_DIV;
665             }
666             break;
667
668         case ':':
669             SetTok (TOK_COLON);
670             break;
671
672         case ';':
673             SetTok (TOK_SEMI);
674             break;
675
676         case '<':
677             NextChar ();
678             switch (CurC) {
679                 case '=':
680                     SetTok (TOK_LE);
681                     break;
682                 case '<':
683                     NextChar ();
684                     if (CurC == '=') {
685                         SetTok (TOK_SHL_ASSIGN);
686                     } else {
687                         NextTok.Tok = TOK_SHL;
688                     }
689                     break;
690                 default:
691                     NextTok.Tok = TOK_LT;
692             }
693             break;
694
695         case '=':
696             NextChar ();
697             if (CurC == '=') {
698                 SetTok (TOK_EQ);
699             } else {
700                 NextTok.Tok = TOK_ASSIGN;
701             }
702             break;
703
704         case '>':
705             NextChar ();
706             switch (CurC) {
707                 case '=':
708                     SetTok (TOK_GE);
709                     break;
710                 case '>':
711                     NextChar ();
712                     if (CurC == '=') {
713                         SetTok (TOK_SHR_ASSIGN);
714                     } else {
715                         NextTok.Tok = TOK_SHR;
716                     }
717                     break;
718                 default:
719                     NextTok.Tok = TOK_GT;
720             }
721             break;
722
723         case '?':
724             SetTok (TOK_QUEST);
725             break;
726
727         case '[':
728             SetTok (TOK_LBRACK);
729             break;
730
731         case ']':
732             SetTok (TOK_RBRACK);
733             break;
734
735         case '^':
736             NextChar ();
737             if (CurC == '=') {
738                 SetTok (TOK_XOR_ASSIGN);
739             } else {
740                 NextTok.Tok = TOK_XOR;
741             }
742             break;
743
744         case '{':
745             SetTok (TOK_LCURLY);
746             break;
747
748         case '|':
749             NextChar ();
750             switch (CurC) {
751                 case '|':
752                     SetTok (TOK_BOOL_OR);
753                     break;
754                 case '=':
755                     SetTok (TOK_OR_ASSIGN);
756                     break;
757                 default:
758                     NextTok.Tok = TOK_OR;
759             }
760             break;
761
762         case '}':
763             SetTok (TOK_RCURLY);
764             break;
765
766         case '~':
767             SetTok (TOK_COMP);
768             break;
769
770         default:
771             UnknownChar (CurC);
772
773     }
774
775 }
776
777
778
779 void SkipTokens (const token_t* TokenList, unsigned TokenCount)
780 /* Skip tokens until we reach TOK_CEOF or a token in the given token list.
781  * This routine is used for error recovery.
782  */
783 {
784     while (CurTok.Tok != TOK_CEOF) {
785
786         /* Check if the current token is in the token list */
787         unsigned I;
788         for (I = 0; I < TokenCount; ++I) {
789             if (CurTok.Tok == TokenList[I]) {
790                 /* Found a token in the list */
791                 return;
792             }
793         }
794
795         /* Not in the list: Skip it */
796         NextToken ();
797
798     }
799 }
800
801
802
803 int Consume (token_t Token, const char* ErrorMsg)
804 /* Eat token if it is the next in the input stream, otherwise print an error
805  * message. Returns true if the token was found and false otherwise.
806  */
807 {
808     if (CurTok.Tok == Token) {
809         NextToken ();
810         return 1;
811     } else {
812         Error (ErrorMsg);
813         return 0;
814     }
815 }
816
817
818
819 int ConsumeColon (void)
820 /* Check for a colon and skip it. */
821 {
822     return Consume (TOK_COLON, "`:' expected");
823 }
824
825
826
827 int ConsumeSemi (void)
828 /* Check for a semicolon and skip it. */
829 {
830     /* Try do be smart about typos... */
831     if (CurTok.Tok == TOK_SEMI) {
832         NextToken ();
833         return 1;
834     } else {
835         Error ("`;' expected");
836         if (CurTok.Tok == TOK_COLON || CurTok.Tok == TOK_COMMA) {
837             NextToken ();
838         }
839         return 0;
840     }
841 }
842
843
844
845 int ConsumeComma (void)
846 /* Check for a comma and skip it. */
847 {
848     /* Try do be smart about typos... */
849     if (CurTok.Tok == TOK_COMMA) {
850         NextToken ();
851         return 1;
852     } else {
853         Error ("`,' expected");
854         if (CurTok.Tok == TOK_SEMI) {
855             NextToken ();
856         }
857         return 0;
858     }
859 }
860
861
862
863 int ConsumeLParen (void)
864 /* Check for a left parenthesis and skip it */
865 {
866     return Consume (TOK_LPAREN, "`(' expected");
867 }
868
869
870
871 int ConsumeRParen (void)
872 /* Check for a right parenthesis and skip it */
873 {
874     return Consume (TOK_RPAREN, "`)' expected");
875 }
876
877
878
879 int ConsumeLBrack (void)
880 /* Check for a left bracket and skip it */
881 {
882     return Consume (TOK_LBRACK, "`[' expected");
883 }
884
885
886
887 int ConsumeRBrack (void)
888 /* Check for a right bracket and skip it */
889 {
890     return Consume (TOK_RBRACK, "`]' expected");
891 }
892
893
894
895 int ConsumeLCurly (void)
896 /* Check for a left curly brace and skip it */
897 {
898     return Consume (TOK_LCURLY, "`{' expected");
899 }
900
901
902
903 int ConsumeRCurly (void)
904 /* Check for a right curly brace and skip it */
905 {
906     return Consume (TOK_RCURLY, "`}' expected");
907 }
908
909
910