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