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