]> git.sur5r.net Git - cc65/blob - src/cc65/scanner.c
Add SB_GetNumber to scanstrbuf.c.
[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 ParseChar (void)
241 /* Parse a character. Converts \n into EOL, etc. */
242 {
243     int i;
244     unsigned val;
245     int C;
246
247     /* Check for escape chars */
248     if (CurC == '\\') {
249         NextChar ();
250         switch (CurC) {
251             case 'b':
252                 C = '\b';
253                 break;
254             case 'f':
255                 C = '\f';
256                 break;
257             case 'r':
258                 C = '\r';
259                 break;
260             case 'n':
261                 C = '\n';
262                 break;
263             case 't':
264                 C = '\t';
265                 break;
266             case '\"':
267                 C = '\"';
268                 break;
269             case '\'':
270                 C = '\'';
271                 break;
272             case '\\':
273                 C = '\\';
274                 break;
275             case 'x':
276             case 'X':
277                 /* Hex character constant */
278                 NextChar ();
279                 val = HexVal (CurC) << 4;
280                 NextChar ();
281                 C = val | HexVal (CurC);        /* Do not translate */
282                 break;
283             case '0':
284             case '1':
285                 /* Octal constant */
286                 i = 0;
287                 C = CurC - '0';
288                 while (NextC >= '0' && NextC <= '7' && i++ < 4) {
289                     NextChar ();
290                     C = (C << 3) | (CurC - '0');
291                 }
292                 break;
293             default:
294                 Error ("Illegal character constant");
295                 C = ' ';
296                 break;
297         }
298     } else {
299         C = CurC;
300     }
301
302     /* Skip the character read */
303     NextChar ();
304
305     /* Do correct sign extension */
306     return SignExtendChar (C);
307 }
308
309
310
311 static void CharConst (void)
312 /* Parse a character constant. */
313 {
314     int C;
315
316     /* Skip the quote */
317     NextChar ();
318
319     /* Get character */
320     C = ParseChar ();
321
322     /* Check for closing quote */
323     if (CurC != '\'') {
324         Error ("`\'' expected");
325     } else {
326         /* Skip the quote */
327         NextChar ();
328     }
329
330     /* Setup values and attributes */
331     NextTok.Tok  = TOK_CCONST;
332
333     /* Translate into target charset */
334     NextTok.IVal = SignExtendChar (TgtTranslateChar (C));
335
336     /* Character constants have type int */
337     NextTok.Type = type_int;
338 }
339
340
341
342 static void StringConst (void)
343 /* Parse a quoted string */
344 {
345     NextTok.IVal = GetLiteralPoolOffs ();
346     NextTok.Tok  = TOK_SCONST;
347
348     /* Be sure to concatenate strings */
349     while (CurC == '\"') {
350
351         /* Skip the quote char */
352         NextChar ();
353
354         while (CurC != '\"') {
355             if (CurC == '\0') {
356                 Error ("Unexpected newline");
357                 break;
358             }
359             AddLiteralChar (ParseChar ());
360         }
361
362         /* Skip closing quote char if there was one */
363         NextChar ();
364
365         /* Skip white space, read new input */
366         SkipWhite ();
367
368     }
369
370     /* Terminate the string */
371     AddLiteralChar ('\0');
372 }
373
374
375
376 void NextToken (void)
377 /* Get next token from input stream */
378 {        
379     ident token;
380
381     /* We have to skip white space here before shifting tokens, since the
382      * tokens and the current line info is invalid at startup and will get
383      * initialized by reading the first time from the file. Remember if
384      * we were at end of input and handle that later.
385      */
386     int GotEOF = (SkipWhite() == 0);
387
388     /* Current token is the lookahead token */
389     if (CurTok.LI) {
390         ReleaseLineInfo (CurTok.LI);
391     }
392     CurTok = NextTok;
393
394     /* Remember the starting position of the next token */
395     NextTok.LI = UseLineInfo (GetCurLineInfo ());
396
397     /* Now handle end of input. */
398     if (GotEOF) {
399         /* End of file reached */
400         NextTok.Tok = TOK_CEOF;
401         return;
402     }
403
404     /* Determine the next token from the lookahead */
405     if (IsDigit (CurC)) {
406
407         /* A number */
408         int HaveSuffix;         /* True if we have a type suffix */
409         unsigned types;         /* Possible types */
410         unsigned Base;
411         unsigned DigitVal;
412         unsigned long k;        /* Value */
413
414         k     = 0;
415         Base  = 10;
416         types = IT_INT | IT_LONG | IT_ULONG;
417
418         if (CurC == '0') {
419             /* Octal or hex constants may also be of type unsigned int */
420             types = IT_INT | IT_UINT | IT_LONG | IT_ULONG;
421             /* gobble 0 and examin next char */
422             NextChar ();
423             if (toupper (CurC) == 'X') {
424                 Base = 16;
425                 NextTok.Type = type_uint;
426                 NextChar ();    /* gobble "x" */
427             } else {
428                 Base = 8;
429             }
430         }
431         while (IsXDigit (CurC) && (DigitVal = HexVal (CurC)) < Base) {
432             k = k * Base + DigitVal;
433             NextChar ();
434         }
435         /* Check for errorneous digits */
436         if (Base == 8 && IsDigit (CurC)) {
437             Error ("Numeric constant contains digits beyond the radix");
438             /* Do error recovery */
439             do {
440                 NextChar ();
441             } while (IsDigit (CurC));
442         } else if (Base != 16 && IsXDigit (CurC)) {
443             Error ("Nondigits in number and not hexadecimal");
444             do {
445                 NextChar ();
446             } while (IsXDigit (CurC));
447         }
448
449         /* Check for a suffix */
450         HaveSuffix = 1;
451         if (CurC == 'u' || CurC == 'U') {
452             /* Unsigned type */
453             NextChar ();
454             if (toupper (CurC) != 'L') {
455                 types = IT_UINT | IT_ULONG;
456             } else {
457                 NextChar ();
458                 types = IT_ULONG;
459             }
460         } else if (CurC == 'l' || CurC == 'L') {
461             /* Long type */
462             NextChar ();
463             if (toupper (CurC) != 'U') {
464                 types = IT_LONG | IT_ULONG;
465             } else {
466                 NextChar ();
467                 types = IT_ULONG;
468             }
469         } else {
470             HaveSuffix = 0;
471         }
472
473         /* Check the range to determine the type */
474         if (k > 0x7FFF) {
475             /* Out of range for int */
476             types &= ~IT_INT;
477             /* If the value is in the range 0x8000..0xFFFF, unsigned int is not
478              * allowed, and we don't have a type specifying suffix, emit a
479              * warning.
480              */
481             if (k <= 0xFFFF && (types & IT_UINT) == 0 && !HaveSuffix) {
482                 Warning ("Constant is long");
483             }
484         }
485         if (k > 0xFFFF) {
486             /* Out of range for unsigned int */
487             types &= ~IT_UINT;
488         }
489         if (k > 0x7FFFFFFF) {
490             /* Out of range for long int */
491             types &= ~IT_LONG;
492         }
493
494         /* Now set the type string to the smallest type in types */
495         if (types & IT_INT) {
496             NextTok.Type = type_int;
497         } else if (types & IT_UINT) {
498             NextTok.Type = type_uint;
499         } else if (types & IT_LONG) {
500             NextTok.Type = type_long;
501         } else {
502             NextTok.Type = type_ulong;
503         }
504
505         /* Set the value and the token */
506         NextTok.IVal = k;
507         NextTok.Tok  = TOK_ICONST;
508         return;
509     }
510
511     if (IsSym (token)) {
512
513         /* Check for a keyword */
514         if ((NextTok.Tok = FindKey (token)) != TOK_IDENT) {
515             /* Reserved word found */
516             return;
517         }
518         /* No reserved word, check for special symbols */
519         if (token [0] == '_') {
520             /* Special symbols */
521             if (strcmp (token, "__FILE__") == 0) {
522                 NextTok.IVal = AddLiteral (GetCurrentFile());
523                 NextTok.Tok  = TOK_SCONST;
524                 return;
525             } else if (strcmp (token, "__LINE__") == 0) {
526                 NextTok.Tok  = TOK_ICONST;
527                 NextTok.IVal = GetCurrentLine();
528                 NextTok.Type = type_int;
529                 return;
530             } else if (strcmp (token, "__func__") == 0) {
531                 /* __func__ is only defined in functions */
532                 if (CurrentFunc) {
533                     NextTok.IVal = AddLiteral (F_GetFuncName (CurrentFunc));
534                     NextTok.Tok  = TOK_SCONST;
535                     return;
536                 }
537             }
538         }
539
540         /* No reserved word but identifier */
541         strcpy (NextTok.Ident, token);
542         NextTok.Tok = TOK_IDENT;
543         return;
544     }
545
546     /* Monstrous switch statement ahead... */
547     switch (CurC) {
548
549         case '!':
550             NextChar ();
551             if (CurC == '=') {
552                 SetTok (TOK_NE);
553             } else {
554                 NextTok.Tok = TOK_BOOL_NOT;
555             }
556             break;
557
558         case '\"':
559             StringConst ();
560             break;
561
562         case '%':
563             NextChar ();
564             if (CurC == '=') {
565                 SetTok (TOK_MOD_ASSIGN);
566             } else {
567                 NextTok.Tok = TOK_MOD;
568             }
569             break;
570
571         case '&':
572             NextChar ();
573             switch (CurC) {
574                 case '&':
575                     SetTok (TOK_BOOL_AND);
576                     break;
577                 case '=':
578                     SetTok (TOK_AND_ASSIGN);
579                     break;
580                 default:
581                     NextTok.Tok = TOK_AND;
582             }
583             break;
584
585         case '\'':
586             CharConst ();
587             break;
588
589         case '(':
590             SetTok (TOK_LPAREN);
591             break;
592
593         case ')':
594             SetTok (TOK_RPAREN);
595             break;
596
597         case '*':
598             NextChar ();
599             if (CurC == '=') {
600                 SetTok (TOK_MUL_ASSIGN);
601             } else {
602                 NextTok.Tok = TOK_STAR;
603             }
604             break;
605
606         case '+':
607             NextChar ();
608             switch (CurC) {
609                 case '+':
610                     SetTok (TOK_INC);
611                     break;
612                 case '=':
613                     SetTok (TOK_PLUS_ASSIGN);
614                     break;
615                 default:
616                     NextTok.Tok = TOK_PLUS;
617             }
618             break;
619
620         case ',':
621             SetTok (TOK_COMMA);
622             break;
623
624         case '-':
625             NextChar ();
626             switch (CurC) {
627                 case '-':
628                     SetTok (TOK_DEC);
629                     break;
630                 case '=':
631                     SetTok (TOK_MINUS_ASSIGN);
632                     break;
633                 case '>':
634                     SetTok (TOK_PTR_REF);
635                     break;
636                 default:
637                     NextTok.Tok = TOK_MINUS;
638             }
639             break;
640
641         case '.':
642             NextChar ();
643             if (CurC == '.') {
644                 NextChar ();
645                 if (CurC == '.') {
646                     SetTok (TOK_ELLIPSIS);
647                 } else {
648                     UnknownChar (CurC);
649                 }
650             } else {
651                 NextTok.Tok = TOK_DOT;
652             }
653             break;
654
655         case '/':
656             NextChar ();
657             if (CurC == '=') {
658                 SetTok (TOK_DIV_ASSIGN);
659             } else {
660                 NextTok.Tok = TOK_DIV;
661             }
662             break;
663
664         case ':':
665             SetTok (TOK_COLON);
666             break;
667
668         case ';':
669             SetTok (TOK_SEMI);
670             break;
671
672         case '<':
673             NextChar ();
674             switch (CurC) {
675                 case '=':
676                     SetTok (TOK_LE);
677                     break;
678                 case '<':
679                     NextChar ();
680                     if (CurC == '=') {
681                         SetTok (TOK_SHL_ASSIGN);
682                     } else {
683                         NextTok.Tok = TOK_SHL;
684                     }
685                     break;
686                 default:
687                     NextTok.Tok = TOK_LT;
688             }
689             break;
690
691         case '=':
692             NextChar ();
693             if (CurC == '=') {
694                 SetTok (TOK_EQ);
695             } else {
696                 NextTok.Tok = TOK_ASSIGN;
697             }
698             break;
699
700         case '>':
701             NextChar ();
702             switch (CurC) {
703                 case '=':
704                     SetTok (TOK_GE);
705                     break;
706                 case '>':
707                     NextChar ();
708                     if (CurC == '=') {
709                         SetTok (TOK_SHR_ASSIGN);
710                     } else {
711                         NextTok.Tok = TOK_SHR;
712                     }
713                     break;
714                 default:
715                     NextTok.Tok = TOK_GT;
716             }
717             break;
718
719         case '?':
720             SetTok (TOK_QUEST);
721             break;
722
723         case '[':
724             SetTok (TOK_LBRACK);
725             break;
726
727         case ']':
728             SetTok (TOK_RBRACK);
729             break;
730
731         case '^':
732             NextChar ();
733             if (CurC == '=') {
734                 SetTok (TOK_XOR_ASSIGN);
735             } else {
736                 NextTok.Tok = TOK_XOR;
737             }
738             break;
739
740         case '{':
741             SetTok (TOK_LCURLY);
742             break;
743
744         case '|':
745             NextChar ();
746             switch (CurC) {
747                 case '|':
748                     SetTok (TOK_BOOL_OR);
749                     break;
750                 case '=':
751                     SetTok (TOK_OR_ASSIGN);
752                     break;
753                 default:
754                     NextTok.Tok = TOK_OR;
755             }
756             break;
757
758         case '}':
759             SetTok (TOK_RCURLY);
760             break;
761
762         case '~':
763             SetTok (TOK_COMP);
764             break;
765
766         default:
767             UnknownChar (CurC);
768
769     }
770
771 }
772
773
774
775 void SkipTokens (const token_t* TokenList, unsigned TokenCount)
776 /* Skip tokens until we reach TOK_CEOF or a token in the given token list.
777  * This routine is used for error recovery.
778  */
779 {
780     while (CurTok.Tok != TOK_CEOF) {
781
782         /* Check if the current token is in the token list */
783         unsigned I;
784         for (I = 0; I < TokenCount; ++I) {
785             if (CurTok.Tok == TokenList[I]) {
786                 /* Found a token in the list */
787                 return;
788             }
789         }
790
791         /* Not in the list: Skip it */
792         NextToken ();
793
794     }
795 }
796
797
798
799 int Consume (token_t Token, const char* ErrorMsg)
800 /* Eat token if it is the next in the input stream, otherwise print an error
801  * message. Returns true if the token was found and false otherwise.
802  */
803 {
804     if (CurTok.Tok == Token) {
805         NextToken ();
806         return 1;
807     } else {
808         Error (ErrorMsg);
809         return 0;
810     }
811 }
812
813
814
815 int ConsumeColon (void)
816 /* Check for a colon and skip it. */
817 {
818     return Consume (TOK_COLON, "`:' expected");
819 }
820
821
822
823 int ConsumeSemi (void)
824 /* Check for a semicolon and skip it. */
825 {
826     /* Try do be smart about typos... */
827     if (CurTok.Tok == TOK_SEMI) {
828         NextToken ();
829         return 1;
830     } else {
831         Error ("`;' expected");
832         if (CurTok.Tok == TOK_COLON || CurTok.Tok == TOK_COMMA) {
833             NextToken ();
834         }
835         return 0;
836     }
837 }
838
839
840
841 int ConsumeComma (void)
842 /* Check for a comma and skip it. */
843 {
844     /* Try do be smart about typos... */
845     if (CurTok.Tok == TOK_COMMA) {
846         NextToken ();
847         return 1;
848     } else {
849         Error ("`,' expected");
850         if (CurTok.Tok == TOK_SEMI) {
851             NextToken ();
852         }
853         return 0;
854     }
855 }
856
857
858
859 int ConsumeLParen (void)
860 /* Check for a left parenthesis and skip it */
861 {
862     return Consume (TOK_LPAREN, "`(' expected");
863 }
864
865
866
867 int ConsumeRParen (void)
868 /* Check for a right parenthesis and skip it */
869 {
870     return Consume (TOK_RPAREN, "`)' expected");
871 }
872
873
874
875 int ConsumeLBrack (void)
876 /* Check for a left bracket and skip it */
877 {
878     return Consume (TOK_LBRACK, "`[' expected");
879 }
880
881
882
883 int ConsumeRBrack (void)
884 /* Check for a right bracket and skip it */
885 {
886     return Consume (TOK_RBRACK, "`]' expected");
887 }
888
889
890
891 int ConsumeLCurly (void)
892 /* Check for a left curly brace and skip it */
893 {
894     return Consume (TOK_LCURLY, "`{' expected");
895 }
896
897
898
899 int ConsumeRCurly (void)
900 /* Check for a right curly brace and skip it */
901 {
902     return Consume (TOK_RCURLY, "`}' expected");
903 }
904
905
906