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